{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2ead1e67-f26f-4a2d-aed8-74f4a0a31fdf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "最终情感标签\n",
      "positive    420\n",
      "neutral     318\n",
      "negative    128\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAJOCAYAAABm7rQwAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAAB6sElEQVR4nOzde5iN9f7/8dc6zFrmPOM0YpypoYkwU/qpEOmgCYVUlEIKRWmnM3ZKsbV10BYdqN0uIYeIoqTQNg6TU2zKjDBymuZojmvdvz98584yMwxxzxo9H9c11zbv+173/XmvWfszq9f63PfYDMMwBAAAAAAAAFjIXtEDAAAAAAAAwF8PoRQAAAAAAAAsRygFAAAAAAAAyxFKAQAAAAAAwHKEUgAAAAAAALAcoRQAAAAAAAAsRygFAAAAAAAAyxFKAQAAAAAAwHKEUgAAAP9n165dmj9/vvbv33/K/dasWaP58+eroKDgvIzDMIzzctwztXv3bu3du/eU++zZs+ecjzcnJ0fp6enn5FhpaWnn5DgAAODcsxn+8q4HAABckNatW6dffvnljB5Tu3ZtXXvttWVu//777xUSEqJWrVqVun348OGSpKeeekq1atUq93nfeustDR06VPPmzVP37t3L3O/WW2/V559/rszMTIWGhpb7+GUpLCzUlVdeqRtvvFEvvfSS7rjjDkVGRurVV19VUFDQKR9733336eOPP9bBgwcVHh7+p8dyop49e+qzzz7Thx9+qLvvvrvE9oKCAjVp0kQul0s//PCDatSocUbHT0tL0+DBg/XUU0+pdevWkqT//ve/6ty5s4YNG6aXX375T/cQExOjKlWq6O2339aVV175p48HAADOHWdFDwAAAFzYZsyYobfeektt2rSR03n6tx5r165Vjx49ThlKTZgwQYsWLdI333yjjh07+mwrLCzUJ598ooCAAL3wwgtnNNbIyEhJUpUqVczav/71L3k8HgUEBMjhcEiSfvvtN0nSxx9/LLvdLsMwVFBQoLy8PA0aNEhhYWFndN6AgABt2bJF1apVk2EYat26tZ577jm53W699tprp3ys3W5Xfn6+AgMDzdrq1au1dOlSValSxRzziQzDUFFRkXJycsoMftauXavPPvtMISEhysjI0NSpU2UYhvLy8tSqVSt16NBBM2fO1N69e3XrrbcqIyNDGRkZkqSioiJlZ2fr8ssvP+XP/J133tGcOXMUFRVlhlJxcXGqUaOGpkyZokcffVRRUVGnff7Ksn37dv3vf/9T7dq1ddlll531cQAAwPlBKAUAAM6r4rBk6dKlql69+in3LSoqUkBAwClXB/3+++9atmyZrrnmmhKBlCR99tlnOnTokAYMGKA1a9aU2F67dm21aNGi1GO73W6f/5WkF198UZmZmXK5XGbAUhy+PP/885KOhzy5ubnKzc1Vz549fUKptLQ0ffbZZ6fsWzoeLh06dEjvvvuuqlWrpgceeEBNmzbV22+/rby8PHXq1EmxsbGljtlut8vlcpm1pKQkjRs37rTnlFRqKJWdna1BgwbJMAxlZWVp6NChPttHjRqlVq1amedYuHChFi5cWOI4hw8fLvNnnpOTo1dffVXBwcF67rnnzLrT6dSYMWPUv39/PfLII5o1a1a5+ijNW2+9JUl6/PHHT7viDAAAWI9QCgAAnFfFQc6ZXNp1qtU17777rvLz8zVx4sQS2zwej7k66t1339W7775bYp8ZM2aUGUrZ7cdvt2mz2XT06FEFBgbql19+UUFBgVwul1wul2w2m/r27auPPvrIXDFVWFio/Px8ORwOnxVLkpSamqpBgwaVq+/NmzeXue/7779faihlt9tls9l8av369dNtt92msLAwhYSE+GwzDEPHjh1Tdna2srOzSxwvPz9fffr00ZYtW/T4449r2LBhaty4sfr27auXX35Z+fn5qlKliu677z79+uuveuaZZ9S5c2e98cYbWr58uWbPni2v16u8vLxTrhibMGGCDh48qKeeeqrEaqh+/frp9ddf16effqobbrhB999/f5nHeemll5STkyOXy1XiuZgxY4ak4yvbygrpvF6vvF6vCgoK1Lx5c/Xt27fMcwEAgHOLUAoAAFjihx9+UERExCn38Xg8pQYvJ27/17/+pR49epj3B3rhhRfUu3dvXXLJJZoxY4a2bdsmm82mNWvWKDo6WtLxIKZ9+/Y6duyY+vTpU+K4L730kr788kv1799fkrRp0yZ17NhR77//voqKisoMik4Og5555pkS4UdAQIAkaebMmbrnnnvK7K1r16765ZdftGPHDknSsWPH9Ouvv6pJkyY6duyYz0qolStXyuv1yu1269ChQ5KO34spLy9PHo9HnTp1KvP+UjabTcHBwQoODi4RBu3atUu9e/fWjz/+qISEBL3yyiuy2+2Ki4vT/PnzNWXKFNWqVUsffvihPv/8c/Xu3dvsNzMzU5999pmys7N12223ldmndDx8e/nll1W1alWNGjWqxHa73a5p06bpyiuv1JAhQ9SoUSN16NCh1GNNmzZNe/bsOeX5JkyYcMrtxe644w5CKQAALEQoBQAAzquioiJJxy8zO/FeTaXxeDw+jznZ+++/r5SUFPNSsW3btmn06NGaNm2aVq9erWeffVYxMTHas2ePPv74Y/N+TLNmzVJycrJeffVVn0vziq1fv14//fSTeU+pyy67TBEREVqwYIFeeeUVzZw5Uy6XS263WzabTZMnT9bKlSs1b948c9wFBQW65JJLShy7+J5ONptNPXv21Ny5c0vss2XLFsXExOjrr7+Wx+ORw+HQF198oV69eukf//iHRo4c6bP/vffeWyKIueqqqyRJ4eHhZ/2X66pUqaLq1avr+uuv16effmquHBs1apSio6NVpUoV9e7dWxdddJFmzpypbt26mY+98sordc0116hq1aqnPEdRUZHuu+8+FRQU6O9//3uZ4VmbNm304osv6sknn1RCQoI++eQTde3atcR+mzdvltPpNFdK2e127dy5Uy1btlS9evW0adMmValSRdOnT1d4eLh69+59Vs8NAAA49wilAADAeXXs2DFJMm9kXR6lXVb266+/atSoUbr77rt16aWXSpL+9re/yTAMvf322xo4cKB+++03zZ49W/PmzdObb76pwYMH66KLLtKTTz6pmJgYDRkypNTzbdu2TfHx8eb3drtdN954o7KysvTtt9+qSpUq8nq9ys/Pl/RHeJaXl2c+xjAMbd26Vdu2bVO/fv3MutfrlXQ8nHr99ddLvYysUaNGio+PV35+vrZu3aqWLVtqyZIlcrvduu+++0rs/+677yo4OFhZWVnq0qWLHA6H1q1bp7y8POXm5urYsWNyu92l3uT8xPEW3+w8PDxcNptNdevW1RdffCGPx+MTIPbo0UOGYeiRRx7R7NmzJR1fVXTiXyiMiorSd999V+b5ij377LPauHGj2rdvX+bPo9ioUaO0Z88e/etf/1K3bt30zDPP6LnnnvO5vPPkSwQNw9DgwYOVn5+vd955x+zjb3/7mxo3bkwoBQCAHyGUAgAA51x+fr4Mw5DL5dJbb71l3nD6TBQUFCg/P18BAQGqUqWK7rrrLqWlpclms2n48OHav3+/lixZogcffFCRkZFavXq1Bg0apKuvvlrNmjXTRx99pDvuuEP16tXTvn379N1335W6SionJ0c///yz7rjjDp/6xx9/rJUrV+r66683/4rdiSFPtWrVNGzYMPN7j8ejoqIihYeH+4RSxau+XC6Xateurdq1a5fab/v27WWz2fTVV1/p0ksv1aJFi9SjR49SVx516tRJkrRo0SKz1qpVK0nS/PnzFRwcfNrn90RZWVnmvacCAgLMSw5P3D5w4EB9+umnio+P13333afhw4dr+/btmjt3rpo0aVKu87z77rt65ZVXVK1aNX344YclLn8szZQpUxQSEqKJEyfq73//u+bPn69XX33VfA5O9uqrr+rbb7/VqFGjdM0115j1KlWq+FwCCQAAKh6hFAAAOOdGjRplXjr3Z73wwgt69tln1bt3b/3www/68ccfVa9ePS1atEiNGjXSxIkTFRISoo0bN+qiiy6SdDwwevvtt9W9e3dt3bpVEydONC9vO9mqVavk9XoVHx9vroAq1r59exUUFPyp8aelpUmSgoKC1KFDB61cubLEPsuWLVPnzp11zTXXaObMmapbt64OHTqkBx988JTH/uabb8x/z5s3T7///rsuv/xyPfroo4qIiFBAQIAZ/DzzzDOKjY3VnXfeKcn3LwaeKqxZvHixHn74YSUnJ+umm27SJ598orCwMLVo0UJ33323LrvsMj300EN66qmnTnkz+/fff18PPPCAHA6H7r//fl155ZUKDAyU0+k8bThVs2ZNTZs2TUOHDtXmzZv17bfflhpKff7553riiSdUp04dXXPNNVq6dKm5raioSJmZmT61YqGhoWrXrt0pxwAAAM4DAwAA4BzbtWuX8cMPPxj//ve/DUnGzJkzje3bt5tfUVFRxj333ONTO/Hrp59+Mn788Udj7dq1xt69ew3DMIysrCzjwIEDhmEYxpgxYwy73W6sXLmy1PMfOnTIuPHGGw1Jhs1mM1q1amXs3Lmz1H2XLl1q9OzZ0zhw4IAxb948Q5KxYsUKn33q169vSDrlV0hISKnHnzlzpiHJSExMNNq3b290797d7HP58uWGJGPZsmWGYRjGnDlzzGO1bdv2lM+xx+MxateubUgy7Ha7UbNmTSMyMtI4cuRIqfs7HA7jjjvuOOUxi2VmZhoffPCBER8fb0gygoODjX/+85+G1+v12S8jI8MYMWKE4XK5jODgYGPIkCHG6tWrDY/HU2KsvXv3NiQZb7/9tvH555+f9vk88Ss+Pt4wDMP44YcfjJtuuskoLCwsMebly5cbwcHBZ3Tc4q9WrVqV63kBAADnFiulAADAOdekSROfS7rq1aunmJgY83un06nIyEjVr19f0vGboBffVLssISEhCgkJ0U8//aSXX35Zjz76qK699lqffYqKivTee+/p6aefVn5+vmbNmiW326177rlHLVq00JNPPqm//e1vCgoKMh9zww036IYbbjjluQMDAxUfH68PPvig1O3Dhg3T5s2bS922YsUKSdLFF18s6fiNyIufi5Nv/H7bbbepfv362rNnjwYPHmzWFyxYoGuvvda8EbskLV26VKmpqQoMDFRBQYHGjRunBx54QC+88IImT54swzBOuQKpoKCgxAqpmTNn6qOPPtL333+vvLw8BQQEKC4uTmFhYdq+fbuGDx/u83MyDEP5+fnq0KGD7Ha75syZo7feeksRERFq1aqVBg0apDvvvFN2u12ffPKJBg4cqOuvv14FBQXKyMhQYGCgdu/erZiYGN1+++2aM2eOz3gOHDig2rVrq06dOpKktm3b6osvvijRy2effaa77rpLDodDrVq1UlJSktauXevT3w033KDatWvr/fff93ls586duawPAICKUtGpGAAAuHD98MMPZa5OGT58uDFy5Mgytz/55JMljpeZmWm0aNHCaNmypZGXl2fWjxw5Yvzzn/80GjdubEgyOnbsaPz888/m9v/9739GbGysIcmoWbOm8fe//91IS0srcfyyVkrFxsYa7du3L7PPbt26GXXr1i1R//33342wsDCjZcuWhmEYRvv27UvtddmyZYbX6zWGDx9uSDLcbrcRGRlpLFy40DCM4yu16tSp47NSKS4uzggNDTX69u1rOBwOo6ioyLjooouM0NBQIyMjwxg5cqRx6623Gps3bzYM44+VUnl5ecbDDz9stG/fvsTKp+3btxsBAQFGtWrVjBEjRhi//vqrMWfOHMPpdBpBQUFGZGSkUa1aNfOratWqRnBwsOF0Oo358+cbhYWFxpdffmk8+OCDRqNGjYzDhw+X+ZwVW7p0qSHJePrpp0ts27hxoyHJGDJkSKmPLSwsNJ588knDZrMZQUFBxtdff23ce++9hiQjNzfXZ986deoY7dq1K3GMqKgo45prrjntOAEAwLnHSikAAHDezZw5U1dccYX5fYcOHSQdv/fUf//7X1WrVk2vvPKKJOm7777T4MGDddNNN/kc4+eff9a9996rzZs3a+zYsZo+fbpSUlL0+++/64EHHtBzzz2nsLAwffjhh7rtttsUExOjHj166L777tPll1+uDRs2aPLkyRo3bpxWrVqlp556qtzjdzgcWrly5SlXHhWv+jrRuHHjlJmZqbvvvluS9MEHH5h/jfBEHo9HHTt21MqVKzVs2DA9+OCD6tatm2699VY1atRIe/bs0YgRI8zzf/DBB1q/fr0ee+wx8y8COhwOvfrqq2rbtq3CwsI0Z84c7d27V5MmTSrRy4oVK7R161bNmDHD56/7xcTEaM2aNWrZsqV5s/O6deuqsLDQ3KegoEBFRUU+q82Sk5MVEREhp9OpLl26qEuXLqd9ToslJiZKki6//PIS2w4cOCBJZd4cfv369Xr77bcVFhamzz//XNdcc02Zq9lO5XSr9AAAwPnBb2AAAHDeuVwuValSxfwqDldq1Kihbt266ccff9Qll1yimJgYbd26VbVq1dLVV1/tc4yff/5Za9askSSNHj1aDz/8sPmX1q688kotXbpUO3fuVN++fbVu3TpJ0uuvv65WrVqpdevWeueddzR48GDt3LlTM2fOlNN5Zp/NxcfHa/v27aV+lXbT7d9//13/+c9/FBkZqYEDB0r64zLG4q/69evrhx9+kN1uV0pKih544AG99tpruvTSS5WUlKTHH39ceXl5atSokZ544glJ0o8//qgHH3xQUVFRJYK1Pn36qEGDBlq/fr327Nmj7t27l/jLeE6n0/xriE888YR5I/ZicXFxZiC1f/9+n5Bnx44dioiI0JQpU3weM3r0aNWqVUsfffTRGT2nkszL8U6+FFP6I5QqvnzvZG3bttV3332nlStX+vylPQAAUDkQSgEAgPPuzjvvVMOGDc2v3377zdx29913KzU1VYsWLVJeXp7mzJmjfv36lVi9cv3112vw4MGaPHmyli1bpt27dysvL0+//PKLJKldu3YKDg6WdPyv5v36669KTEzUsGHD9PPPP2vo0KFau3atatWqpVq1ap1xD0FBQT6B0olfISEhJfaPjIzUli1b9O9//9vnXlAnevTRR3X//ffr559/VmJiot5++22z79DQUE2cOFH79+/XL7/8Yv5lwaCgIPOvC1avXr3U477xxhuSpIcffrjU7ddcc4369u2rI0eOaPTo0WX2/Pe//10DBgxQSkqKpOP3xQoJCdHy5cvNfYqKirRkyRJVr15dPXv2LPNYpVm7dq3++9//qn379oqKiiqx/XQrpSQpNjZWLVu2NL83DOOMxnCm+wMAgHOHUAoAAJx3K1askGEY5ledOnV08OBBLViwQLVr19a9996rkSNH6pVXXtGRI0c0bNiwEsdwOByaOnWqhg8frs6dO6thw4al3qB6xYoV2rZtm6Tjq5veeOMN7du3TzNmzDijy8pOVnz5XmlfCxYsKPUx1apV07XXXqt27drp008/lXR8NVNISIiysrI0YsQIBQYGavDgwXK73SUev2bNGo0YMcKndvHFF2v9+vXq1q1bqefctWuXPv74Y8XGxpqXSZbmmWeekc1m09SpU/XTTz+Vus9TTz0lm82myZMnSzp+mdv111+vNWvWqKioSJK0fPly82dWWg9lKSgo0IMPPijpeDhXmvKEUic78VLD09m7d68yMjLMlWEAAMBahFIAAOC88Xq9kqRly5Zp/PjxeuCBB3T99dfr0KFDmjVrll5//XVJ0iuvvKKCggKNGTNGffv2Vb169U553D179mju3LkaNWqUrr76ajM0yc/P17333qvY2Fi1atVKr776qg4dOqSwsDDde++95R5vaatnTnX5Xu/evcs85ty5c7VmzRr9/PPPko5fPpeTk2OuvBo3bpxycnL0v//9T5KUl5ennJwcScdXKr322muaOHGizzFPXFVUHPQVGzFihAoLC0uEWSeLiYlR165dddlllykvL6/UfRo0aKDu3bvrP//5jxlCtW3bVtnZ2dq0aZMkacaMGXI4HOrfv/8pz3cij8ej/v3768cff9Stt95aZsC2Z88eSWVfvleagoKCUusnPkcLFixQZGSk6tWrp/z8fDVv3rzcxwcAAOcONzoHAADn3LZt29S5c2cdPHhQkjRhwgTFxsYqPj5evXr1UmJiovr166c333xTkrRp0yb9/vvvuuyyy/Thhx/K6XTqb3/7m5o2bWoe85///Kc+/fRTbd++XRkZGZKOr56KiYkx9/N6vZowYYIWL16sRYsWaeTIkRo1apS6du2qhx56SF26dDnlzcqzs7MllQw2PB6PGSKVxuFwlHnMKVOmmKuhJCk8PNw8psPh0COPPKIePXqoYcOGko6vjrr33ns1YcIEffTRR4qLi9NTTz2ltm3blnrfpIKCAnm9XvN4o0aNUq1atdSvXz+lp6fr999/16ZNm+TxeBQYGOjz2Pfff1/VqlU75XMybNgw3XnnneY+d9xxh66//nrFxMTo0KFDmj9/vm666Sbz8sLT+e2333Tvvffqq6++UosWLfThhx+W2McwDP3444/69ttvFRkZWeblj6Upa6VUUVGRPB6PJOmWW26RYRhq3Lixbr75Zo0ZM6bcxwcAAOcOoRQAADjnLr30UnXr1k1NmzbVVVddpVatWvkEIiNHjlR+fr4OHz6sV199Va+++qoGDRqk119/Xd98840GDhyo6dOnKz4+Xm+99Zbi4uJUp04drV+/Xp06ddL111+vq6++Wi1atPA5bmBgoPr06aM+ffqosLBQS5cu1ZQpU7Rw4UItWLBAbdq0UWJiYpl/bS0rK0uSzL9oV6ysoKOwsFB9+/bVokWL1KhRoxLbFyxYoHXr1umhhx5StWrVJEnR0dGSpOeee84ndCs+7/vvv699+/YpKyvLvHfUDTfcoI8//rjUUKp4bPn5+QoKCtK1115r3jR84cKF6tWrl7nvyTePL+2eVB9++KFWrVoll8vlE7atXLmyxL4//fST8vPzlZWVpWHDhqmoqEiFhYUqKirSpEmTfI5/9OhRTZkyRZMmTVJmZqauueYaLViwQGFhYT7H7NOnj2bPnm2uWnvooYdKnPdUyrqUsqCgwHyuHA6HDh8+zGV7AABUMEIpAABwXkydOrXMbbm5ueZNyufMmaN33nlH/fr1kyR17txZO3fu1OzZs7Vq1SrFxcVJOr66Zd++faXeELs0AQEBSkhIUEJCgpKSkjRq1Cg1bNiwzECqWFRUVIl9yrq8LSAgQC6XS61atdIzzzxTYnvXrl01Z84cxcfHm7U77rhD7777riZMmFDqMe12uzp06GBebtilSxd9++23at++fan7FwctBQUFCgoK8tl2yy23KDY2VjExMUpISNA999xTRtd/SExM1IwZM+RyueRyuU65iko6ft+srVu3auvWrfJ4PCosLFReXp7Gjh1bIvT68ssvlZeXp+eee07PP/98qX8BccSIEfr666/VunVr3XnnneUac3kUv+aKEUgBAFDxbAZ/cgQAAFQgr9d72qDoXMnLy1OVKlUs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6lq5cqUk6eDBg+rTp4+qVaumqlWrqnfv3jpy5Igk3pcDxQilgArw4osvatKkSZo6daqWLVum+++/X9LxXyz33HOPbr/9dn3xxRdq0qSJOnfurA0bNkiSsrKydMsttyg6OlpLlizR+PHjNXXqVE2fPr1c523Tpo3WrVundevWSZKmTp2qdevW6fPPPy/32Lt3767CwkLzl60kff7557rkkkvUsmVLSdJ7772nRx99VCNGjNAXX3yhmjVr6rrrrtPRo0fLfR4AqGz69esnm82mefPm6ZprrtHgwYN15MiRPz0nvv3221q3bp0GDRqk1q1bm/N4QkJCiX0nTZqkv//97+rTp4/69Olj1pOTk9WtWzc1b95cX331lZ588kmNHDmyxAcfAPBXc7Zzd1ZWlnr06KGbbrpJCxYsUHJyshITE7V48WLFxsaax/7vf/+rDz/8UJ988ol++uknjRo1ShLvywGTAcAyK1asMCQZH3zwgVl77733DEnG3r17jQYNGhh33XWXuc3j8RixsbFG9+7dDcMwjOTkZEOS8cknn5j7rFq1yti6dWuJc917773GvffeW+ZYJBkrVqw45XjL2qdHjx7GI488Yn7fsWNHY/To0eb39evXN4YPH25+//vvvxsOh8N47733Tnk+AKiMiufmhIQEs3bo0CFDkrFq1arTzomlzdejR4822rdvf9rayWNo1KiRcejQoRLbd+7cafzrX/8yMjMzDcMwjKysLCM6Otp45ZVXSux7ut8fAHAh+LNz97p16wxJxrFjxwzDMIy33nrLuPjii33OMW3aNGPdunXm98OHDzeaNWtWYiy8L8dfGSulgArQrl0789/x8fGSpF9++UUpKSnq3Lmzuc1ut6tjx47mJygNGjTQLbfcovvvv1+33Xabxo8fr4iICF166aWWjr9Pnz5atGiRJCkjI0OrVq3SHXfcIen4p0Z79uzRa6+9Zi5JjoyMlMfj0a5duywdJwBYadiwYea/a9SoIUlKS0s7qznR6/We1Rheeukl89wnatq0qdq1a6fx48frmmuuUc2aNbV//34dO3bsrM4DABeKs527GzZsKLfbrYULFyo7O1vLli1T8+bNfY7dp08f/fe//9Vtt92m2rVr6/XXXz/n8y7vy1HZOSt6AMBfkWEY5r9P/g+P4uvKi9ntdp/9Fy5cqO+++07ff/+9li5dqmeeeUYffPCB+vbte34HfYLiYGz79u3asmWLmjVrpmbNmvnsM27cOHXt2tWnVrNmTcvGCABWa9y4cZnbznRO3Lt371mN4corryy1/vnnn+u2225Tr1691L9/f02dOlVDhw49q3MAwIXkbOfu4OBgNW/eXPfcc48KCgp0ySWXaPHixeZ+WVlZatWqlYKCgtS3b18NGTJEv/zyi8aPH39Ox8/7clR2hFJABfj+++/NX4CJiYmy2Wy6+OKLVb9+fX399dfmDW+9Xq9WrFhhrqbavHmzPvnkE7300ktq3769nn32Wd1www2aPn36GYdSbrdbubm5ZzX+oKAgJSQkaNGiRdqyZYv5aYwkhYaGql69ejp69Kguv/xysz5s2DBde+216t2791mdEwD8ncPhKFErz5zodDqVmZlpbsvJydGSJUsUExPjc6wqVaqc9bz93nvv6aqrrtJ//vMfSVJeXp727NlzVscCgAvJ2c7db731lho3bqwVK1boyJEjatiwoez2Py5E+uabb/TLL7/ol19+UaNGjSRJy5cvL3UMvC/HXxmhFFABnn76aQUFBckwDD377LPq1auXLrroIv3973/Xfffdp3r16qlTp0567733tGPHDr333nuSjv9iefXVV+V0OnXDDTcoNTVVGzduLPVmt6dz5ZVXavr06QoNDdXevXtVr149n8sKT+eOO+7QK6+8ol9++UWjR4/22TZ69GgNGTJEF110kdq2bat58+bp7bffVr9+/c54nABQ2Z1uTmzZsqXGjBmj3bt3KzIyUgMGDPBZIVvsyiuv1PPPP6/PPvtMYWFh2rZtm4YPH16uMVSvXl2rVq3SF198oby8PE2YMEEpKSkqKio6p70CwIXidHN3cHCwVq9erSVLlqhhw4bKy8tTvXr1FBoaKun4vCtJM2fO1HXXXae5c+dqypQpuuiii0qci/fl+Eur2FtaAX8txTc6f++994z69esbgYGBRq9evYyjR4+a+3zwwQdG06ZNjYCAACMuLs5YuXKlzzG++OIL44orrjBCQkKMqlWrGnfddZfP44ud7ka1//vf/4yrr77acLvdRo0aNYwvvviixD46xU0X8/LyjPDwcCMuLq7U7W+88YbRqFEjo0qVKkarVq2MRYsWlTkWAKjMim+Wm5yc7FM/cQ491ZyYm5tr9OnTxwgNDTWio6ONF154wXj++edLvan5uHHjjJo1axoBAQFGly5dTjuGYgcOHDBuuukmIzg42GjUqJExatQoo3v37qWegxudA/gr+LNz9759+4zw8HBzTpZk2O12429/+5u5zwsvvGBERUUZkZGRxs0332z885//NOx2u/HLL7/4nJP35fgrsxlGKR/FATgvvv32W3Xs2FGHDx82Pz0BAAAAULlcc801uvjii9W/f39VqVJF2dnZmj59upYuXaq0tLSKHh5QaXD5HgAAAAAAZ2DUqFF66aWXlJCQoOzsbIWHh6tNmzaaNWtWRQ8NqFRYKQUAAAAAAADL2U+/CwAAAAAAAHBuEUoBAAAAAADAcoRSAAAAAAAAsByhFAAAAAAAACxHKAUAAAAAAADLEUoBAACcxgcffKCrr75a2dnZZe5z7bXX6o033vhT51mxYoVSUlIkSUuXLlVRUdEp909OTtaNN96oNWvW/KnzFsvNzVXz5s311FNPlbr9yy+/1I033qikpKRzcj4AAPDX5qzoAQAAAPi7Y8eOafXq1XI4HJKk/fv36+DBg3K73WZt3bp1at68uXbs2CFJys/PV35+vq644opyn2fo0KFq2rSppk+frrvvvltNmjTRkiVLVLVq1VL3z8/P15dffqlHHnnErO3atUsZGRkKCgqS3e77+aPX61VBQYGqVaumunXrljjeP//5T23fvl3t27fX8uXLZRiGcnNzdfnll6tevXoaO3asNm3apJCQEDOgKywslNfrVbVq1crdJwAAgEQoBQAAcFqBgYGSpCpVqkiSZsyYoXHjxqlKlSpmKJWfn68PPvhAc+bMkSTl5eWpqKhIeXl55nF2796tgoKCU56rqKhIaWlpmjVrlmbPnq3ffvtNv/76q6pXr67o6Giffd1ut8+4JOnFF1/UzJkzT3mOF154Qc8++6xPbfPmzXrppZcUGRmp999/XzNnzlR4eLjy8vL0r3/9S2vXrtUPP/wgSbr44ot9HnvDDTdo6dKlpzwnAADAyWyGYRgVPQgAAAB/ZBiGbDab/v3vf6tfv346evSofv31V11++eXaunWrXC6XXC6XJKlFixa6//77NWLECHk8HhUUFKh69eqqUaOGebyLL75Yu3btOquxjB49WmPGjPGppaSkqGHDhlqxYoU6dOggSTp8+LAkKTIyUk7nH58/er1eZWdnKysrS0FBQYqMjDS37dixQ9dff71cLpfWr1+vp556Sl9//bV27twpm82mjRs36rrrrlObNm00f/58LVy4UIMGDdLPP/9sBnYnHg8AAKA8uKcUAABAGT755BNFRkbq999/lyRNmzZN1113nfLy8nTZZZepefPmuvTSSxUbG6usrCy99dZbio2NVfPmzdW8efMSK5YcDofGjh0rwzBK/XrkkUcUHx9vfv/LL78oKytLx44d05NPPmke51//+pfeffddffbZZ5Kkr776Su+++64+/fRT1ahRQzVq1PAJpCTJbrcrLCxMderU8QmQkpKSFBcXp6ysLM2bN0+RkZG65pprlJOTo+TkZB08eFA7d+5Uw4YN9Z///EehoaHq2rWr8vLy9PnnnysyMpJACgAAnBVWSgEAAJRhzJgxev/99/Xiiy+qX79+2rBhg9q0aaOlS5eqU6dOPsFPRESERowYYa5mKg6WTryvU/PmzXXnnXdq+/btZqBU7KmnnlLt2rU1cuRI/f7773I4HGrVqpUCAwNL3Mjc6XQqMjJSeXl5ys7OVtWqVWUYhpo0aaLExMQz7vO9995Ts2bNdNVVV0k6fg8tl8ulgwcPqlWrVgoICNC6detUu3Zt8zHz5s1T165dzZViAAAAZ4pQCgAAoAw9evRQQECAbr31VvXr109FRUXatGmThgwZoh9//FEul8sMnTIyMuR2u837OxmGocLCQnXt2lWzZ8+WJMXExOiee+7R008/Xer5tm/frubNm+uHH35QtWrVdPHFF+s///mP7rzzTp/9vF6v7Ha7+vfvr5kzZ5qX723dulX//e9/fcZ1Mq/Xq/z8fF111VWKjY0ts/d9+/bphhtu0N69exUeHq5q1appwYIFql+//hk/jwAAAKXhRucAAACl8Hq9Wr16tZ544gmz5nA41Lp1a82aNUuGYSggIMC80fnJPB6PioqKfFZT5eXlye12q0OHDlq5cqXP/oMHD9bUqVN18cUX65133pHb7VatWrV0++23lzh2ceC0du1ac6x79+7VqlWr9Mgjjyg4ONgcV0ZGhux2u0JDQyUdv5F6bm6uXn/99TJDqS+//FL33nuvcnJytGDBAl188cW69dZbFRcXp0mTJqlv375lhl4AAADlRSgFAABQiqNHj+qWW25Rx44dtX37dp9t9evX18CBA/Xuu++W+Xin06nCwkLze8Mw9Ntvv5nh0OjRozVixAhJ0pAhQ8z9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",
      "text/plain": [
       "<Figure size 1200x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "from snownlp import SnowNLP\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "from matplotlib import rcParams\n",
    "\n",
    "# 加载数据\n",
    "data = pd.read_csv('奶茶话题评论.csv')\n",
    "\n",
    "# 处理缺失值：将缺失的评论文本替换为空字符串\n",
    "data['text'] = data['text'].fillna('')\n",
    "\n",
    "# 定义积极和消极关键词列表\n",
    "positive_keywords = ['好喝', '喜欢', '爱喝', '不错', '推荐']\n",
    "negative_keywords = ['难喝', '不喜欢', '不喝', '糟糕', '失望']\n",
    "\n",
    "# 初始化情感计数\n",
    "data['积极词数'] = 0\n",
    "data['消极词数'] = 0\n",
    "\n",
    "# 遍历每条评论，统计关键词出现次数\n",
    "for index, row in data.iterrows():\n",
    "    for keyword in positive_keywords:\n",
    "        data.at[index, '积极词数'] += row['text'].count(keyword)\n",
    "    for keyword in negative_keywords:\n",
    "        data.at[index, '消极词数'] += row['text'].count(keyword)\n",
    "\n",
    "# 计算每条评论的情感倾向得分（简单相减）\n",
    "data['情感倾向得分'] = data['积极词数'] - data['消极词数']\n",
    "\n",
    "# 根据情感倾向得分划分标签\n",
    "data['简单情感标签'] = 'neutral'  # 默认标签\n",
    "data.loc[data['情感倾向得分'] > 0, '简单情感标签'] = 'positive'\n",
    "data.loc[data['情感倾向得分'] < 0, '简单情感标签'] = 'negative'\n",
    "\n",
    "# 定义情感标注函数（SnowNLP）\n",
    "def get_sentiment(text):\n",
    "    if not text.strip():\n",
    "        return 'neutral'\n",
    "    try:\n",
    "        s = SnowNLP(text)\n",
    "        sentiment = s.sentiments\n",
    "        if sentiment > 0.7:\n",
    "            return 'positive'\n",
    "        elif sentiment < 0.3:\n",
    "            return 'negative'\n",
    "        else:\n",
    "            return 'neutral'\n",
    "    except Exception as e:\n",
    "        print(f\"错误：无法处理文本 '{text}'，错误信息：{e}\")\n",
    "        return 'neutral'\n",
    "\n",
    "# 应用SnowNLP情感标注函数到数据\n",
    "data['SnowNLP情感标签'] = data['text'].apply(get_sentiment)\n",
    "\n",
    "# 整合情感标签：优先使用简单情感分析的结果\n",
    "data['最终情感标签'] = data.apply(lambda row: row['简单情感标签'] if row['简单情感标签'] != 'neutral' else row['SnowNLP情感标签'], axis=1)\n",
    "\n",
    "# 查看整合后的标签分布\n",
    "print(data['最终情感标签'].value_counts())\n",
    "\n",
    "# 设置Matplotlib支持中文字体显示（以SimHei为例，需确保系统中安装了该字体）\n",
    "rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签\n",
    "rcParams['axes.unicode_minus'] = False  # 用来正常显示负号\n",
    "\n",
    "# 可视化最终情感标签分布\n",
    "plt.figure(figsize=(12, 6))\n",
    "sns.countplot(data=data, x='最终情感标签', color='skyblue')  # 使用color参数而不是palette\n",
    "plt.title('最终情感标签分布', fontsize=16, fontweight='bold')\n",
    "plt.xlabel('情感标签', fontsize=14)\n",
    "plt.ylabel('数量', fontsize=14)\n",
    "plt.xticks(fontsize=12)\n",
    "plt.yticks(fontsize=12)\n",
    "plt.grid(axis='y', linestyle='--', alpha=0.7)\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "# 保存数据到CSV文件\n",
    "data.to_csv('final_labeled_comments.csv', index=False, encoding='utf-8-sig')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "ba04ce0d-f380-41d8-863b-dfeb3d542107",
   "metadata": {},
   "outputs": [],
   "source": [
    "##如果两个positive和negtive标签不一致，以简单情感分析为主，其余以SnowNLP为主"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "6f730609-f6b5-4095-a788-a05f15e9f96f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9oAAAMyCAYAAACSALmoAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAADuCUlEQVR4nOzdd3xUZdrG8d+kJyShd6R3CyIgIoKI2EUQpQqI7iq7dre8u6676lpX3bWturqW1dW1K4j03hEQkN4S0guQ3pOZOc/7x5CRSAIhyeSkXF8+85GcOXPOPZNI5prnOffjMMYYRERERERERKRG+NldgIiIiIiIiEhDoqAtIiIiIiIiUoMUtEVERERERERqkIK2iIiIiIiISA1S0BYRERERERGpQQraIiIiIiIiIjVIQVtERERERESkBiloi4iIiIiIiNQgBW0RERERERGRGqSgLSICuN3uaj3+008/pW/fvjz11FNn3Pc///kPffv25bnnnqvWOcsTExPDhg0bOHbsGAB79+5l69atlX680+nk+++/Z9euXTVe28/997//JTMz87T7FBUV8f333/usBqfTSVxcXI0e83//+x+DBw/m5ZdfpqCgoEaPLSIiIvWDgraI1AmHDh3iwIEDZ307dOjQGY+dlpbGxx9/jGVZFe4zZ84chg4dyrx586pUf0BAAAcPHiQrK+uM+xYWFnLw4EHy8vKqdK7T2bp1K5dddhmPPPIIALNmzWLYsGE8/fTTp33+pbKyshg2bBizZs2q8dpOtnTpUm6//Xa6d+9OfHx8hfu99957DBs2jKuuugpjTJXP9+ijj/L2229TWFjo3bZt2zY6dOjAtGnTqnzc8nz77bds27aNr776itDQ0Bo9toiIiNQPAXYXICICMGTIEHJycs76cU2bNj1juP3mm2+YPXs277zzDmvWrCl3ny+//JItW7awc+dObrrpprOuIyIiAoDg4GDvts2bN/P6668TGhpKUFAQAQGef3L37t0LwMqVK3nooYcAz4h6UVERhYWF/OlPf6J///5nXQPgDXaRkZEAfPHFF9x2223Mnz+fBx980FvnmR7/84C4YcMGJk6cSFhYmPd5lKf0efzud7/jwQcfLHcfy7L405/+BMCVV17Jvn372LdvH5ZlkZ+fT4sWLbjyyitxuVy8+OKLANxxxx04HI4y5yksLMTf3/+MYTYr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woULCQ0N9V7PC5wydbyU2+3G6XSSn5/PSy+9ROvWrcvcB54p5B988EG5S4OVXl8+ZMgQFixYwA8//MDIkSNZtGgRAPfcc0+5zb6+/PJLwsPDcTgc3H777fz3v//l0ksvZfXq1YBnOnp+fj4lJSUEBARU+JqXcrlcFBUV4Xa7y1wHPnbsWMaOHXvK/uHh4WRmZjJ27FiOHz9Os2bNePPNNwkNDeWZZ57xXoffsWNHOnbseNpzg2d698SJE8nIyGD48OHepc5Ox8/Pjy+//JJbbrmFBQsW8Itf/IJVq1Z5fzZOZ+PGjbz55pt06NCBN954w7u9SZMmAJVq2CciIiINi4K2iNiioKCAzMxMwsLCCA0NpXPnzlU+Vk5OjndprE6dOmFZFtOnT8ftdpOens748eNJT09n586dBAYG8uGHH7J161aKiorKBO4HHniAV155hSlTpvDdd9/RoUMHXn/99QrPu2PHDgBvkC81ZswYxowZw7hx41ixYgWhoaH4+/uXaZzVsWNH4uPjvWtIG2O8QTsvL4+nn366zDFLSkoACAkJITk5udxrel0uF+BpHgawYMECRo4cyeeff05AQEC5134DZa6r3rJlyyn3b9myhREjRlT4OlTktttu4+OPPz7jfkeOHGHcuHHs2bOHsWPH8s4773DNNdfw97//neXLl/PGG29w6aWXVuqcbrebmTNnsn79elq1asUnn3zi/YDjTIKDg5k7dy6/+tWveO+99/j4449ZunQpTz31FL/85S/L/YAhMTGRW265BX9/fz777LMy176Xnvd0y6GJiIhIw6Tf/iJii5UrV5Y7ulldKSkptGvXjrvuuotnn32Ww4cPc/7553P48GFyc3N56qmnuPDCC7nwwgsZOHAgXbp08T72ueeeY8mSJXz11Vf4+/vzxRdf0KZNmwrPVTriO3jw4HLX8v72229r7HkdO3YM8Iz+3nrrrd5lr05Wuqb4ZZddRuvWrfnf//7Htddey86dO7nlllto3779ac+RlJTEwYMHvV9v3rwZf39/AgMD6dWrF82bNyc4ONgbODMzM9m1axft27end+/e3scVFRVRXFxMp06dzvi8Pv74Yx544AEyMzO59tpr+eKLLwgJCWH16tVMmjSJZcuWMXz4cC6//HIeeeQRrrnmmgqP5XQ6mTZtGl999RWBgYG88MILDBw4sEyH8MqYOXMmr7zyCr/73e84duwYGzdu5O677z5lv/z8fG666SZSU1MJDQ09ZVm20iXOvvnmGzZu3FjuuR555BFuv/32StUlIiIi9YeCtojYonfv3jz55JOEhoby6aefsn37dv75z3+W2efVV18lKirqlO0/V1xc7B3RDg8PBzyj01OnTuW8885j//79DBgwgMGDB5dZQql///5ljrNkyRLv8kxut5uPP/6YwYMHn3Zt5Z49e3Leeeexfv36cu/fvHkzl1xyyWnrLxUeHk5ubm6598XGxtKyZUvvNOoxY8bwr3/9y3v/H/7wB1asWAF4RlJvu+02XnnlFSZMmADgXVP7dD799FOMMQAcOHCAyy+/nP79+7N161YOHTp0yv7Lly/nqquu4qabbuKtt96q1HMstWLFCp566inWrFkDwP33389LL73kHf1t1qwZixYt4sknn+TZZ59lzZo1rFmzhi5dujBhwgTGjBnDoEGDaNu2rfeYCQkJHDhwAIfDwX/+8x+GDh1KRkYGGRkZZ1VbSEgIDz74IEOGDOGxxx4r8zqXys7O5vrrr/fOaigsLCzzIcXP983Ozi73vvz8/LOqTUREROoJuy8SFxGZPHmyKe+fo8svv9wAxu12m+PHj5vCwkJvl+fKcrvdZsSIESYkJMTs3bu33H1SU1PN9OnTDWDat29vvvnmG2/DsT59+lSqa/TJzdBOtmfPHgOYwYMHmxdffLHCW/PmzU3Lli3LPXZaWprx8/Mzw4cPN8Z4uoqPGzeuzD633367adq0qffrpKQkExwcbABzxRVXlNm3tEnaySzLMueff763sdqIESPM4MGDDWDeeOONcutatmyZAczs2bPP8OoYb0fxp556yvTp08d7nvDwcPPhhx+agoKCcr+3JSUlZv369RU2gGvXrp3Jycnx7p+Tk2Pmzp1rjDHG5XKZjIwMU1RUZFwul7fD/N///vdya5w0aZIBzEcffXTa55KammoGDhxoANOrVy9z6aWXGsDs3r27zH7r1q0zgPnFL35xyjEef/xxA5h33333jK+diIiI1D8a0RaROqP0+uWfy8jIKNMY7Oc6dOhAUlJSuff94x//YN26dbz22munjGAnJSXx2muv8frrr1NQUMDNN9/MW2+9RZs2bbjhhht48MEHeeuttxg9ejTXX389f/7zn8tdi/l0Skegzz33XH73u99VuN/rr7/uvcb657766issy2L06NHebfHx8XzwwQfer6Oioso8JjMzk/DwcIqLizHGkJOTQ2RkJFFRUYwdO5a//e1vjBs3zrv/119/ze7duxk6dCibN2/Gz8+Pf/7znwwbNownnniC6dOnk5WVxYIFC5g2bVq5DcKOHTvGv//9bx599NFTvpczZszgf//7n/frNm3a8PDDD/PFF19w++23V2r69JIlS/jiiy/48ssvycnJAWD69OllrjGPiIjwPi9/f3+aN2/uvS8mJgagwvXAk5OTAU7bcG3t2rVMnTqV5ORkevTowcqVK7nvvvvOWHtFSn8+REREpGFR0BaROuOjjz4q8/UzzzzDgQMHiIiI4LHHHuPJJ5/kgQce8Hb5zszM5IEHHuCGG24o93jz5s3jT3/6E9deey333XcfxhhSUlIoKSnhgw8+4Omnn8btdtOpUyc++OADAgMD+fHHHxkzZgxBQUH861//4oorruA3v/kNCxcuZOHChbz33nvceeedlX5OpdcF7969m7/97W8V7pednV1ueHU6nbz44osATJo0ybt9x44d3HHHHWX2LX38ypUrmTRpEtnZ2fTp04fVq1czaNAg3njjDbZu3cqBAwf46quvvIHU5XLxl7/8BYDf//733HrrrQBccsklDB48mH79+lFQUMCnn37KH//4R15//XX27NlzSp1Dhw71TnH/9a9/Xeb+N998k4ULF9K7d29mzZrF7bffTmhoKHv37sWyLIKDg8tc/12qpKSE4uJiiouLufjii7n66qt54403WLJkCXPnzuWqq66q8DU9mTGGH3/8EYDzzjuv3H1SUlKAioN2fn4+99xzD8nJyfTv359ly5bRoUOHSp2/IhV9uCQiIiL1nM0j6iIiZ5w6bowxRUVFJjw83Dz88MPe+999910DmOXLl5/y2Pfee8/4+fkZwPTu3dv06NHDO5V6xowZJi8vz/Tr1888+uijJi8vz7hcLu+U5nPOOcc8+uijJioqyhhjTF5ennn00UfNlClTKnwOFU0dj4mJqfSa4F26dDnluI8++qgBzJVXXlmZl9I8+uijxuFwmIiICLNgwQKTn5/vfX1Pvq1atcr7mHvuuccAZtiwYcYYYwBz+eWXG2OMyc7O9u5XOl26dLrzz6eOv/POO97p4LGxsafUdqa1uC3LMgcOHDCHDh065b7PPvvMHDhwoFKvQXm2b9/u/d5WpHQN8fKm1pfKyckx9913n0lPT/duGzduXJWnjp9pmrqIiIjUT6dfDFVEpBY5HI4yt9JGWeBZemny5Mn897//9TYM++ijj+jcuTNXXHHFKccaMGCAd13uw4cPU1RUxJAhQ5gyZQpjxoyhSZMm7N69m6effpomTZpQVFTEPffcw5VXXklycjLPPPMMvXr1YvTo0fz44488/fTTfPrpp1V+brfffjvGmApvJ3c/P9l5551Hv379ePbZZys8dlZWFjNmzODvf/87Dz30EHfeeSfbt2/n+uuvJywsjM8++4xPP/2UAQMG4Ofnx6RJk7xLgK1cuZI333yT4OBg3n333VOOHRkZCcDevXvZsWMH7du3r3CZsF/+8peMHDmSvLw8Zs+efcr9zZo18/49PT2dv/zlLyxZssS7bdeuXfTt2/eUKfalz69v374sXLiwwtfhdP79738DVDj7ITs7m4KCApo1a3bada8jIiL45z//SYsWLapUh4iIiDQOmjouInXG/v37y3w9c+ZMtm7dSn5+Pi6Xi9/+9rd8+OGHPProo1x77bWsWbOGv//97+Wubzxo0CD++9//0rdvX84//3xCQkJO2efk5Z6aNGnCAw88wAMPPEBqaioffvghr7/+OqtXr66RUBUXF8dXX31V4f0FBQXlBrwpU6YwefJk9uzZw4YNGxg+fDgA7733Hrt37+bOO++kZcuWLF68mE8//ZSLL7643MA8ZcoUevXqRZ8+fbyd2QFGjx7N7373O3r37n3KNewnKw36999//2mvK/7nP//JwIEDWbJkCV9++SUTJ04sd7+8vDz+8Y9/MGfOHMaMGYO/vz8DBgygZ8+eLF++nOLiYm+3988++wyn08nEiRO5/vrrKzx3RQ4cOOC9nr28Zbrgp+uzK7Mk2c85nc6zfkzp0l+aOi4iItJA2TiaLiJijCk7dTwjI8Ns2bLFfPLJJ6Znz54GMCEhIebbb781xhjzyCOPGMC0atXKdOzY0eTl5VXpnCd3uF6xYoX5+OOPTzmW0+msVMdxY3w3dbzUnXfeaQDz2muvGWOMmTZtmgHM4sWLjTHGfPfddwYwnTp1KtOFu9THH39sAgMDzcyZM8/4XDhp6rgxxmzZssX4+fmZ8PBwk5GR4d1eUdfxqVOnGsB07NjR5ObmVnieJ554wgDmk08+8W57+OGHDWCWLVvm3VY6ZX3Pnj1nrP3ncnNzzaBBgwxgpk6dWuF+K1asMIC59tprz/ocY8aMqdTU8f3795v333/fPPfcc6Zdu3YGMPPnzz/r84mIiEjdp6njImILy7L48ssvefHFF9m5cycALVu2pEWLFlx88cXccccd3uZUL7zwgrcB2p/+9CeaN29OWloazZo14/Dhw2c8V2FhIZs2beKf//wn06ZNo0uXLqxduxbwNMn6v//7P6ZPn06bNm2YPn06S5cuxbIsAgICyp2WXp6ioiLAs/72ycyJdaknTJjA4cOHK7xdcMEFFR47NzeXL774guDgYG9DtNIR+tDQUABuvPFG7rzzTn71q195R6zT09P573//izGGYcOGER4ezn//+1/efPPNCs9VWn/ptHvwrMFtWRazZ88u08W7oi7p//d//0evXr144YUXTjsN+4EHHiA0NLRMN/KRI0fSrFkzYmNjAVi3bh07duzg6quv5txzz63wWOXJysripptuYtu2bXTt2pVXX321wn1LfwZP13G8IiUlJeVu//lr2bRpU371q1/xyCOPkJqaSqtWrbjsssvO+nwiIiJS92nquIjYonT5qHXr1uFwOOjbty9Dhw7lkksuYciQIVxwwQVMmTKFb775htmzZxMUFER6ejq33HILxcXFvPTSS7zxxhsMGjSIW265hV//+tdcfvnl3mnkL7zwAps2bWLfvn1ER0eXCcDNmzf3LhdWXFzMXXfdRYcOHVi+fDn/+9//+N///kenTp244447uOuuuzjnnHPO+HxKrxv/eegqDaMRERH07NmzwseXTiUuz9tvv01eXh533nknbdu2BTzLY4FnCnapd999t8xU5HXr1nH77bfz1ltvsXHjRj755BOuu+46fvOb33DppZdy4YUXnnKu4uLiU57Hd999x3vvvcftt99OYWEh33//Pfn5+bz//vsAp0zLv/DCCzlw4EC5U/pP1rx5c1auXOn9EAXgpptuIiMjw/s8SqesP/TQQ6c91s+tXLmSu+++m+joaNq2bcvChQtPWSJu8+bNuN1u4uPj+cc//gHA+eeff1bnAejZsyf5+fneDz1Klf7Mlf4MtG/fngkTJpCVlcVll13G7bffXm6neREREan/FLRFxDbPPPMMWVlZDB8+vNzroEtHibOysvjyyy958sknsSyLpUuXMnz4cO6++25+//vf88477/Dll19yySWXsGbNGoKCgigoKGDu3LkEBAQwZMgQrrjiCgYNGsRFF11Et27dvOcICQlh9uzZzJ49m/z8fBYsWMC7777L8uXLeeqpp3j22We59dZbee+992jSpEmFz6WioF36HCqyZs0aHnvsMQ4fPsygQYPKfQ1ee+01HA5HmSZhpc3THnvsMZYtW+YdOS+Vn5/Pt99+C+C9rvvaa69l+vTpLFiwgPj4+EoH7RYtWvD73/8e8IzO/upXv+LQoUPe+6+55ppTjlNeyH788cd59913CQkJISAgoFLXJx88eBDwjH673W5cLhdOpxOXy8Vrr73G1KlTy+y/YcMGnn76aRYvXgxA//79mT9/fpnveakPP/yQf/3rX96vO3bsyPTp089Y08+999575W4v/d6XvqZAtRrqiYiISP2hoC0ithkxYsRp7z85oBw/fpzevXvz6aefehtWNWnShDfffJM//elPvPHGG9x4443eRl3Tpk2jdevWTJ06tdLNzJo0acKkSZOYNGkShw4d4sknn+STTz4hKirqtCEbPAG0bdu2p4zuFhQUnPZxQ4cOJTo6mhtuuIE///nPp9wfEhLC3r17WbVqFf369fNuHz9+PG+++Sbbtm1j27Zt5R7bz8+Piy++mD/84Q/ebS+//DIvvvgi7dq1K/cx5QXtnx/z9ttvZ+HChQwePJhJkyZx6aWXnvY5liosLCQ9PZ2QkBBCQ0PLNKOrSOlU7tIRf5fLRVFREUVFReXW2LlzZ2JjY/H39+eee+7h+eefP2WkudS9997LZ599Rt++fbnhhhu4++67admyZaWeS2WU1nymnwERERFpeBzm58MgIiJ1lGVZZ5yOXNN27NhBdna2dzksqfuioqIoLCys0jRwERERkZqgoC0iIiIiIiJSg9R1XERERERERKQGKWiLiIiIiIiI1CAFbREREREREZEapKAtIiIiIiIiUoMUtEVERERERERqkIK2iIiIiIiISA1S0BYRERERERGpQQraIiIiIiIiIjVIQVtERERERESkBiloi4iIiIiIiNQgBW0RERERERGRGqSgLSIiIiIiIlKDFLRFREREREREapCCtoiIiIiIiEgNUtAWERERERERqUEK2iIiIiIiIiI1SEFbREREREREpAYpaIuIiIiIiIjUIAVtERERERERkRqkoC0iIiIiIiJSgxS0RURERERERGqQgraIiIiIiIhIDVLQFhEREREREalBCtoiIiIiIiIiNUhBW0RERERERKQGKWiLiIiIiIiI1CAFbREREREREZEapKAtIiIiIiIiUoMUtEVERERERERqkIK2iIiIiIiISA1S0BYRERERERGpQQF2FyAiIiIiIlJfud1unE6n3WWIjwUGBuLv71/p/RW0RUREREREzpIxhtTUVLKysuwuRWpJs2bNaNeuHQ6H44z7KmiLiIiIiIicpdKQ3aZNG8LCwioVvqR+MsZQUFDAsWPHAGjfvv0ZH6OgLSIiIiIichbcbrc3ZLds2dLucqQWhIaGAnDs2DHatGlzxmnkaoYmIiIiIiJyFkqvyQ4LC7O5EqlNpd/vylyTr6AtIiIiIiJSBZou3riczfdbQVtERERERESkBiloi4iIiIiISK2bNWsWs2bNqtYxHA4Hq1evrpF6apKaoYmIiIiIiNSkf//b7grg7rvtruCMnnjiiTPeP2vWLLp27VrhPlu3bqVPnz41W1gN0Ii2iIiIiIiI1LquXbueNkT/9a9/JTY29rTHGDx4MBERETVbWA1Q0BYRERERERGpQQraIiIiIiIiAsDq1atxOBy8/vrrnHPOObRo0YKZM2eSnZ3t3efjjz+md+/eBAcHc/HFF7Nu3boyx1izZg1Dhw6lSZMmtG/fnt///ve43e5TzlXeNdql5y/t8H3FFVfgcDgqHPku7xrtjz76iMjISEpKSrzbDh8+jMPhYOfOnd5tb7zxBj169KBJkyZcdtll7NixozIvUaUoaIuIiIiIiEgZzzzzDP/4xz946623WLZsGXfeeSfgCbEzZ87klltuYeHChfTs2ZMxY8awbds2AHJzc7nxxhvp1KkTixYt4rnnnuOtt97inXfeqdR5Bw0axNatW9m6dSsAb731Flu3buW7776rdO3jx4/H6XSyZs0a77bvvvuOPn36MGDAAADef/99Hn74YR566CEWLlxImzZtGD16NOnp6ZU+z+moGZqIiIiIiIiU8cILLzBp0iQA8vPzufPOO0lMTOSxxx5j6tSpPPfcc4BnxHn37t08/fTTzJkzh/T0dPLy8pg0aRIjR45k5MiR9OrVi2bNmlXqvBEREQwePNj7dZ8+fcp8XdljXHfddcyfP5+rrroKgPnz5zNlyhTvPk8++ST33HMP999/PwADBgygVatWzJs3jzvuuOOszlcejWiLiIiIiIhIGcOHD/f+fciQIQBER0cTGxvLmDFjvPf5+flxxRVXeEegu3btyo033sidd97JhAkTeO6552jWrBnnnnturdY/ZcoU5s+fD0B2djbr169n8uTJgGfUPS4ujldffdU7Tb158+a43W4OHz5cI+dX0BYREREREZEyjDHev1uWVea+0uunS/n5+ZXZf968eSxcuJCLLrqIxYsXc/755/Pxxx/7tuCfufHGGzl69Cj79+9nyZIl9OvXj379+pXZ5+mnn2bHjh1lbvfdd1+NnF9BW0RERERERMo4ucHZli1bcDgc9O7dmy5durBixQrvfZZlsWrVKu+o965du3j00Ue5/PLL+fOf/8yaNWu46qqrKn2N9smCg4MpLCysUv1hYWGMHTuW+fPnM3/+fO9oNnimlnfu3Jn09HQuvPBC7+3dd99l/fr1VTrfz+kabRERERERESnjT3/6E2FhYRhj+POf/8zEiRNp3749Tz75JHfccQedO3fmyiuv5P333+fAgQO8//77gCfEvvTSSwQEBHDNNdeQnJzM9u3bGTt27FnXMHToUN555x0iIiJISEigc+fOZaa0n8nkyZN5/vnniY6O5vHHHy9z3+OPP84999xD+/btueSSS5gzZw5vv/02M2bMOOs6y6OgLSIiIiIiImU888wz/N///R/Hjh3jxhtv5F//+hcAM2fOxOFw8NRTT/Hiiy8yYMAAli1bxqBBgwDo1q0bc+bM4YknnuDll18mKCiIa6+9lr///e9nXcM777zDL37xC8aMGUNkZCQffvjhWT3+uuuuY9asWfTq1YsePXqUue/OO++koKCAl19+mccee4x+/foxd+5chg4detZ1lsdhTp5MLyIiIiIiIqdVVFRETEwM3bp1IyQkxO5yatTq1au54oorOH78OK1atbK7nDrlbL7vukZbREREREREpAZp6riIiIiIiIgAMGrUKDTpufo0oi0iIiIiIiJSgxS0RURERERERGqQgraIiIiIiIhIDVLQFhEREREREalBCtoiIiIiIiIiNUhBW0RERERERKQGKWiLiIiIiIiI1CAFbREREREREZEaFGB3ASIiIiIiIg3J7Pl2VwBv32h3BXXDrFmzAPjggw9q9bwa0RYREREREZFatXr16loPv7VJQVtERERERERqlYK2iIiIiIiIiFSagraIiIiIiIgAEBsbi8PhYPfu3cycOZPIyEg6derEhx9+6N3njTfeoEePHjRp0oTLLruMHTt2eO+bNWuW97roUk888QSjRo0CYNSoUTgcDv7617+yZs0aHA4HDoejzOh2aQ2xsbF88MEHXHDBBfziF78oc8wlS5YwaNAgwsLC6Nq1K6+88kpNvxTVoqAtIiIiIiIiZcyYMQOHw8GcOXMYMWIEs2fPJi0tjffff5+HH36Yhx56iIULF9KmTRtGjx5Nenp6pY779ttvs3XrVu666y4uuugitm7dytatWxk7duwp+/7jH//gySefZMqUKUyZMsW7PSYmhnHjxtG/f3+WLl3KH//4R37729+ybt26Gnv+1aWu4yIiIiIiIlJG586dvaPYF1xwAZ999hkHDx7kySef5J577uH+++8HYMCAAbRq1Yp58+Zxxx13nPG4ffr0AWD+/PkcOnSIwYMHV7jvwoUL2bx5M61bty6z3eVy8corr3DbbbcRERHBhRdeyDPPPMOmTZsYMWJEVZ9yjdKItoiIiIiIiJRx3333ef9eGnQzMjKIi4vj1Vdf9U75bt68OW63m8OHD1d4LMuyqlTDs88+e0rIBujVqxfDhw/nueeeY8SIEbRp04akpCQKCgqqdB5f0Ii2iIiIiIiIlNGjR48K73v66ae54YYbymxr06ZNhfsnJCRUqYahQ4eWu/27775jwoQJTJw4kVmzZvHWW29x7733VukcvqKgLSIiIiIiImX4+/ufsi0iIoLOnTuTnp7OhRde6N1+3333MXLkSCZNmkRAQAA5OTne+/Lz81m0aBF9+/Ytc6yQkBAKCwurVNv777/PsGHD+OSTTwAoKioiLi6uSsfyFU0dFxERERERkUp5/PHHefPNN3nxxRdZt24dv/nNb3j77bfp0qUL4Llme8WKFRw5coTMzExmzJiBMeaU4wwdOpQdO3bwzTffsHz5cl599dVK19CqVSv279/PwoUL+eabbxg1ahSxsbG4XK4ae57VpRFtERERERERqZQ777yTgoICXn75ZR577DH69evH3LlzvdO877rrLjZu3MiFF15I06ZNmT17Nueffz5r1qwpc5wrrriCxx9/nF//+tdkZmZyxRVX8OCDD1aqhqeeeoqkpCQmTZpE27ZtmThxIu3bt2f9+vU1/nyrymHK+3hBREREREREylVUVERMTAzdunUjJCTE7nKklpzN911Tx0VERERERERqkIK2iIiIiIiISA1S0BYRERERERGpQQraIiIiIiIiIjVIQVtERERERESkBiloi4iIiIiIiNQgBW0RERERERGRGqSgLSIiIiIiIlKDFLRFREREREREalCA3QWIiIiIiIg0JP/m33aXwN3cbXcJtcLhcLBq1SpGjRpldyllaERbRERERERE6pwnnniC2NjY0+6zdetWBg0aVDsFnQUFbREREREREalz/vrXv54xaA8ePJiIiIjaKegsKGiLiIiIiIiI1CAFbREREREREQEgNjYWh8PB7t27mTlzJpGRkXTq1IkPP/zQu88bb7xBjx49aNKkCZdddhk7duwoc4w1a9ZwwQUXEBkZya233sof/vAHWrVqxZo1awA4evQoU6ZMoWXLlrRo0YJJkyaRlpYGwOrVq3E4HDgcDgCuuOIKHA4HXbt2Lbdeh8PB6tWry2z76KOPiIyMpKSkxLvt8OHDOBwOdu7cWennUR0K2iJ1yKxZsyr8R0REREREpLbMmDEDh8PBnDlzGDFiBLNnzyYtLY3333+fhx9+mIceeoiFCxfSpk0bRo8eTXp6OgC5ubncfPPNXHfddXz77bfExMSwZcsWFixYwHnnnec99vfff89HH33EZ599xr59+/jDH/4AwKBBg9i6dStbt24F4K233mLr1q189913la59/PjxOJ1Ob7AH+O677+jTpw8DBgwAOOPzqC51HRfxsaysLF555RXGjx/PhRdeeNp9Z8+ezfjx42ulLhERERGRinTu3Nk7in3BBRfw2WefcfDgQZ588knuuece7r//fgAGDBhAq1atmDdvHnfccQcHDx4kMzOTJ554gtDQUH75y1/yyiuvMHToUO+xJ06cyMCBAxk8eDAAY8aMYenSpQBERER4twP06dOnzNeVERERwXXXXcf8+fO56qqrAJg/fz5Tpkzx7nOm51FdGtEW8bGsrCz++te/8uOPP55x32HDhiloi4iIiIjt7rvvPu/fW7duDUBGRgZxcXG8+uqr3undzZs3x+12c/jwYQC6detGcHAw8+bNIy8vj2XLltG/f/8yx54yZQrff/89EyZMoEOHDrz22msUFBTUaP1Tpkxh/vz5AGRnZ7N+/XomT54MeEbdz/Q8qksj2iIiIiIiIlJGjx49Krzv6aef5oYbbiizrU2bNgA0adKE/v37M3PmTEpKSujTpw8LFizw7pebm8vAgQMJCwtj+vTp3HPPPURHR/Pcc8/VaP033ngjd955J/v372f37t3069ePfv36Vfp5VJdGtKXeczgcPPHEE6xdu5aLL76YsLAwevfuzZw5c8rs53K5eOaZZ+jZsyehoaH079+f1157DcuyyuwXGxvL+PHjadasGeeccw7PPPMM9957LxEREXzwwQfe/ebOncvQoUMJDw+nffv23HbbbaSkpHjvHzVqFA6Hg27dugFwxx13eD8xe+KJJ8p9LhVdo52bm0tYWBgPPPBAme2WZdGxY0cmTJhQZvtXX33F4MGDCQ0NpUuXLtx///1kZ2ef6aUUEREREQHA39//lG0RERF07tyZ9PR0LrzwQu/t3XffZf369QC8+eab9OjRg2PHjhEVFcW+ffvKhPaVK1cSHR3N3Llz+b//+z/GjBlDTExMuTUEBwdTWFhYpfrDwsIYO3Ys8+fPZ/78+d7R7Mo+j+rSiLY0CD/88AMvvfQSs2fPZsaMGbzwwgtMmzaNqKgoOnbsCHimjyxatIgHHniAHj16sGnTJh588EFSU1N59tlnvccaN24cmZmZPP3002RkZPDUU08xatQo3n77be811osXL2bChAmMGDGCl156iaysLP7+979z2223sXLlSgAeffRRfvnLX5KWlsbDDz/M3XffzYgRIwDPdS5nIyIigptuuokvv/ySV155BT8/z2dk69evJzk5mdtuu8277+uvv87999/PjBkz+NWvfkVcXBwvv/wy27dvZ8OGDVV+jUVEREREHn/8ce655x7at2/PJZdcwpw5c3j77beZMWMG4BnR3rBhA4sWLaJbt24UFRXRuXNn71rXrVq1AuDDDz9k9OjRfP3117zxxhu0b9/+lHMNHTqUd955h4iICBISEujcuTPDhw+vdK2TJ0/m+eefJzo6mscff/ysnkd1KWhLg7BgwQI++eQTpk6dCkCXLl0YN24cmzZt4tZbb2XVqlXe/4knTZoEeLoRxsTE8Prrr/PUU0/h7+9PRkYGu3bt4rXXXvNel7Jz507279/PtGnTvOc7cOAA48eP58MPP/T+owHwxz/+kby8PMLDw72NF2JjY3n44YcZNmwY06dPr/JznDZtGp9//jlr165l1KhRAHz++ec0bdrUO+UlNzeXRx55hGnTpvHSSy+VefzTTz/Nli1buPjii6tcg4iIiIg0bnfeeScFBQW8/PLLPPbYY/Tr18870xM8U7b/8Ic/8OCDD5KZmYnT6cTPz4/f/va3vPDCCwwfPpynnnqK119/nX/+858MGzaMf/zjH/z2t7/lyJEjdO/e3Xuud955h1/84heMGTOGyMjIMkuMVcZ1113HrFmz6NWr1ylT4c/0PKrLYYwxNXIkEZs4HA4GDRrEDz/84N125MgRevTowX/+8x9mzZrFI488wt/+9rcKjxEdHe39n7p79+706NGDt956i8zMTMaPH8/AgQPLXVIgJSWFjRs38v333/Pxxx+TmppKTExMmenfsbGxdOvWzVvL6cyaNYvVq1cTGxt7yn1Op5N27doxadIk/vWvf+F2u+nYsSPXX38977//PgBLlizh2muvrfD47733HnfeeedpaxARERGR0ysqKiImJoZu3boREhJidzl1yogRI+jduzezZs0iJCSEvLw83nnnHRYvXkxGRobd5VXL2XzfNaItDcIll1xS5uvSqdWl0tLSAJgzZw7h4eGnPP7kpgdXXXUVH3zwAT179gSgf//+vPrqq2X237NnD3fddRfff/89kZGRDBo0iCFDhpzV+n5nKzAwkIkTJ/LNN9/w+uuvs2bNGo4ePVpm2njp83ziiSfKnVbTp08fn9UnIiIiIvKHP/yBZ599lrFjx5KXl0fTpk0ZNGgQn3/+ud2l1SoFbWkQSq/1qEjLli0BT9A8udtgamoqsbGxlE7sWLlyJf/5z39ITU0lKiqKsLAw+vXrd0oziJtvvpmSkhK2bNnC4MGDcTgcfPDBBz4N2uCZPv7222+zatUqvvzyS9q3b88VV1xxyvNs06YNY8aM8W4vKChg165dp3wAISIiIiJSk2688UZuvPFGu8uwnYK2NApXX301zz//PB999FGZxmcPPvggX3/9NcePHwfg8OHD+Pn50aRJkwqvZU5LSyMqKorZs2czZMgQAIqLi/noo4/K3b80/Obk5FT7eYwYMYLOnTvz8ccfs2DBAmbMmFEmPF966aWEh4fzySefMHv2bO99H374Iffccw/z5s3zNoeT+sXCophiSn72p7xtpX8MZa8MOvnrn99X3jaDwR9/AgkkgIAy/y39E1DcjIL0cwgNhLBACA2A0EBoEgj++lxHREREGikFbWkURo8ezS233MJzzz1HfHw8I0eO5IcffuCL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siXuOcTCnZur+x56SMWlpXr95ZfV096u6vr6uA9JC0t61TS11DC0zjDkZCs5ACSNaVn61LL01siI+rq7tWjFCt138qTKKiuTPTUAABKKHx1POn369M3IlqTHHntMkvTmm2/q17/+tSTpn/2zf3bz97OysvRP/+k/lc/n06VLlyRJq1atUmFhoX70ox/pf/2v/6WWlhY1NDToRz/6kbZv3z6L/zXfdfToUZWVleknP/mJJCkSiej//J//o0ceeUQ5OTmSpNdff12jo6P6+7//e1VWVt78+uEPfyhJ+uSTT2Zlrk6nU1v37dOxxx9XTm6u2q9dUyQcntGYX1qWzpqm/KxuA0BSjFmWzpumLvX1ydPXp427dunBp54isgEAaYkV7Unz5s37xr9XVFRIkoaHh9XX16f8/HyVfOsE1Pr6eklSb2+vpOiq+C9/+Uv98R//sb7//e9rfHxcVVVV+r3f+z398R//cVK3xLlcLp0+fVo//elP9Vd/9Ve6dOmSent7v7FtfGByq/Yf/dEfaefOnd8ZY+nSpbM2X8MwtGztWhUWF+vcL36hG9euaV5dnXLz8+Me06PoVvItDocaWdkGgFnTZVl6LRzW9Y4OOZ1OHThxQht27ZLT6Uz21AAAsAUr2pO6urq+8e9T8VxaWqrKykr5/f7vHAjW2dkpSaqqqrr5vX379unChQsaHR3Vhx9+qBMnTuiHP/yhfvSjH9n8X3BvTz75pPr6+nThwgX95Cc/UU1Njfbv33/z98vLyyVF/3vuu+++m187duxQQUFBUn5QUDd/vh75nd/R2i1b1N/dLU9fn2byRLoJRbeSv26ammB1GwBsFbEsvWeaOjs+rqstLSosKtIDTzyhzXv3EtkAgLRGaE/6h3/4B/n9/m/8uyTt2rXr5j3M//W//tebvx8Oh/WjH/1I+fn52rt3ryTpxz/+serr69XX16esrCytWbNGf/ZnfyZJeuedd+Ka11T83noCerx2796txsZG/e3f/q1++tOf6nvf+9434nkqqP/+7//+G4em/c//+T+1fft2vffeezOeQzwKiop09PRpHXzoIZmRiDpbWxWJRGY0Zptl6TnTVDexDQC28E3esvPuyIg6rl1T48KFevjZZ7V45cpkTw0AANuxdXyS3+/Xvn379P3vf18fffSR/tt/+286fPiwduzYoa1bt+p//I//oR/8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",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib import rcParams\n",
    "\n",
    "# 设置Matplotlib支持中文字体显示（以SimHei为例，需确保系统中安装了该字体）\n",
    "rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签\n",
    "rcParams['axes.unicode_minus'] = False  # 用来正常显示负号\n",
    "\n",
    "# 尝试使用不同的编码格式读取数据\n",
    "try:\n",
    "    data = pd.read_csv('final_labeled_comments.csv', encoding='utf-8-sig')\n",
    "except UnicodeDecodeError:\n",
    "    try:\n",
    "        data = pd.read_csv('final_labeled_comments.csv', encoding='gbk')\n",
    "    except UnicodeDecodeError:\n",
    "        data = pd.read_csv('final_labeled_comments.csv', encoding='gb2312')\n",
    "\n",
    "# 统计最终情感标签的数量\n",
    "label_counts = data['最终情感标签'].value_counts()\n",
    "\n",
    "# 绘制饼图\n",
    "plt.figure(figsize=(10, 10))\n",
    "\n",
    "# 定义颜色和阴影\n",
    "colors = ['#ff9999', '#66b3ff', '#99ff99']\n",
    "explode = (0.1, 0, 0)  # 突出显示第一个标签\n",
    "\n",
    "# 绘制饼图并添加阴影\n",
    "patches, texts, autotexts = plt.pie(\n",
    "    label_counts,\n",
    "    labels=label_counts.index,\n",
    "    autopct='%1.1f%%',\n",
    "    startangle=140,\n",
    "    colors=colors,\n",
    "    explode=explode,\n",
    "    shadow=True,\n",
    "    textprops={'fontsize': 14, 'color': 'black'}\n",
    ")\n",
    "\n",
    "# 调整标签字体大小\n",
    "for text in texts:\n",
    "    text.set_fontsize(14)\n",
    "\n",
    "# 调整百分比标签字体大小\n",
    "for autotext in autotexts:\n",
    "    autotext.set_fontsize(12)\n",
    "    autotext.set_color('white')  # 设置百分比标签为白色\n",
    "\n",
    "# 添加图例\n",
    "plt.legend(\n",
    "    patches,\n",
    "    label_counts.index,\n",
    "    loc='upper right',\n",
    "    bbox_to_anchor=(1.3, 1),\n",
    "    fontsize=12\n",
    ")\n",
    "\n",
    "# 设置标题\n",
    "plt.title('最终情感标签分布', fontsize=18, fontweight='bold', pad=20)\n",
    "\n",
    "# 显示图形\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "36e61f2d-2427-4735-b506-51de3e412b00",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib import rcParams\n",
    "\n",
    "# 设置Matplotlib支持中文字体显示（以SimHei为例，需确保系统中安装了该字体）\n",
    "rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签\n",
    "rcParams['axes.unicode_minus'] = False  # 用来正常显示负号\n",
    "\n",
    "# 尝试使用不同的编码格式读取数据\n",
    "try:\n",
    "    data = pd.read_csv('final_labeled_comments.csv', encoding='utf-8-sig')\n",
    "except UnicodeDecodeError:\n",
    "    try:\n",
    "        data = pd.read_csv('final_labeled_comments.csv', encoding='gbk')\n",
    "    except UnicodeDecodeError:\n",
    "        data = pd.read_csv('final_labeled_comments.csv', encoding='gb2312')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "df9530ec-aaaa-49cb-8bab-2488c8731fa3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>weibo_id</th>\n",
       "      <th>comment_id</th>\n",
       "      <th>created_at</th>\n",
       "      <th>text</th>\n",
       "      <th>like_count</th>\n",
       "      <th>user_name</th>\n",
       "      <th>user_id</th>\n",
       "      <th>user_gender</th>\n",
       "      <th>follow_count</th>\n",
       "      <th>followers_count</th>\n",
       "      <th>source</th>\n",
       "      <th>积极词数</th>\n",
       "      <th>消极词数</th>\n",
       "      <th>情感倾向得分</th>\n",
       "      <th>简单情感标签</th>\n",
       "      <th>SnowNLP情感标签</th>\n",
       "      <th>最终情感标签</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>PpWFpgaY4</td>\n",
       "      <td>5161839290222291</td>\n",
       "      <td>2025-05-02 12:57:32</td>\n",
       "      <td>小朋友的奇妙世界</td>\n",
       "      <td>5</td>\n",
       "      <td>草莓味的草莓来一碗</td>\n",
       "      <td>7990256715</td>\n",
       "      <td>女</td>\n",
       "      <td>10</td>\n",
       "      <td>0</td>\n",
       "      <td>来自广东</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>neutral</td>\n",
       "      <td>positive</td>\n",
       "      <td>positive</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>PpWFpgaY4</td>\n",
       "      <td>5161838623065287</td>\n",
       "      <td>2025-05-02 12:54:53</td>\n",
       "      <td>哇塞，酷炫</td>\n",
       "      <td>6</td>\n",
       "      <td>亿碗月光</td>\n",
       "      <td>7990326454</td>\n",
       "      <td>女</td>\n",
       "      <td>6</td>\n",
       "      <td>0</td>\n",
       "      <td>来自广东</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>neutral</td>\n",
       "      <td>positive</td>\n",
       "      <td>positive</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>PpWFpgaY4</td>\n",
       "      <td>5161837873333156</td>\n",
       "      <td>2025-05-02 12:51:54</td>\n",
       "      <td>预约</td>\n",
       "      <td>7</td>\n",
       "      <td>食食如意ww</td>\n",
       "      <td>7989820867</td>\n",
       "      <td>女</td>\n",
       "      <td>18</td>\n",
       "      <td>3</td>\n",
       "      <td>来自广东</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>PpWFpgaY4</td>\n",
       "      <td>5161840213492529</td>\n",
       "      <td>2025-05-02 13:01:12</td>\n",
       "      <td>预约起来了</td>\n",
       "      <td>3</td>\n",
       "      <td>亿碗热干面面面</td>\n",
       "      <td>7990256694</td>\n",
       "      <td>女</td>\n",
       "      <td>9</td>\n",
       "      <td>0</td>\n",
       "      <td>来自广东</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>PpWFpgaY4</td>\n",
       "      <td>5161840986031289</td>\n",
       "      <td>2025-05-02 13:04:15</td>\n",
       "      <td>等人少的时候再去，现在估计人很多</td>\n",
       "      <td>1</td>\n",
       "      <td>吃葡萄不吐橘子皮哇</td>\n",
       "      <td>7990326470</td>\n",
       "      <td>女</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>来自广东</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>861</th>\n",
       "      <td>PpWybeUpR</td>\n",
       "      <td>5161854426942224</td>\n",
       "      <td>2025-05-02 13:57:41</td>\n",
       "      <td>#周一到周日都不能喝奶茶吗#</td>\n",
       "      <td>0</td>\n",
       "      <td>这里取了个名字</td>\n",
       "      <td>7167913878</td>\n",
       "      <td>男</td>\n",
       "      <td>96</td>\n",
       "      <td>35</td>\n",
       "      <td>来自安徽</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>862</th>\n",
       "      <td>PpWybeUpR</td>\n",
       "      <td>5161851761199138</td>\n",
       "      <td>2025-05-02 13:47:04</td>\n",
       "      <td>转发微博</td>\n",
       "      <td>0</td>\n",
       "      <td>东湖子非君</td>\n",
       "      <td>7256727188</td>\n",
       "      <td>男</td>\n",
       "      <td>233</td>\n",
       "      <td>64</td>\n",
       "      <td>来自安徽</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>863</th>\n",
       "      <td>PpWybeUpR</td>\n",
       "      <td>5161848362763484</td>\n",
       "      <td>2025-05-02 13:33:35</td>\n",
       "      <td>添加剂</td>\n",
       "      <td>0</td>\n",
       "      <td>WZQ翌翔</td>\n",
       "      <td>5621168251</td>\n",
       "      <td>男</td>\n",
       "      <td>56</td>\n",
       "      <td>23</td>\n",
       "      <td>来自安徽</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>neutral</td>\n",
       "      <td>positive</td>\n",
       "      <td>positive</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>864</th>\n",
       "      <td>PpWybeUpR</td>\n",
       "      <td>5161840729654710</td>\n",
       "      <td>2025-05-02 13:03:15</td>\n",
       "      <td>少喝</td>\n",
       "      <td>0</td>\n",
       "      <td>6666Zyc</td>\n",
       "      <td>7714551751</td>\n",
       "      <td>男</td>\n",
       "      <td>125</td>\n",
       "      <td>10</td>\n",
       "      <td>来自安徽</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>865</th>\n",
       "      <td>PpWybeUpR</td>\n",
       "      <td>5161835197632510</td>\n",
       "      <td>2025-05-02 12:41:16</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>通海乐梅人</td>\n",
       "      <td>1793857883</td>\n",
       "      <td>男</td>\n",
       "      <td>24</td>\n",
       "      <td>75</td>\n",
       "      <td>来自安徽</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "      <td>neutral</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>866 rows × 17 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "      weibo_id        comment_id           created_at              text  \\\n",
       "0    PpWFpgaY4  5161839290222291  2025-05-02 12:57:32          小朋友的奇妙世界   \n",
       "1    PpWFpgaY4  5161838623065287  2025-05-02 12:54:53             哇塞，酷炫   \n",
       "2    PpWFpgaY4  5161837873333156  2025-05-02 12:51:54                预约   \n",
       "3    PpWFpgaY4  5161840213492529  2025-05-02 13:01:12             预约起来了   \n",
       "4    PpWFpgaY4  5161840986031289  2025-05-02 13:04:15  等人少的时候再去，现在估计人很多   \n",
       "..         ...               ...                  ...               ...   \n",
       "861  PpWybeUpR  5161854426942224  2025-05-02 13:57:41    #周一到周日都不能喝奶茶吗#   \n",
       "862  PpWybeUpR  5161851761199138  2025-05-02 13:47:04              转发微博   \n",
       "863  PpWybeUpR  5161848362763484  2025-05-02 13:33:35               添加剂   \n",
       "864  PpWybeUpR  5161840729654710  2025-05-02 13:03:15                少喝   \n",
       "865  PpWybeUpR  5161835197632510  2025-05-02 12:41:16               NaN   \n",
       "\n",
       "     like_count  user_name     user_id user_gender  follow_count  \\\n",
       "0             5  草莓味的草莓来一碗  7990256715           女            10   \n",
       "1             6       亿碗月光  7990326454           女             6   \n",
       "2             7     食食如意ww  7989820867           女            18   \n",
       "3             3    亿碗热干面面面  7990256694           女             9   \n",
       "4             1  吃葡萄不吐橘子皮哇  7990326470           女             4   \n",
       "..          ...        ...         ...         ...           ...   \n",
       "861           0    这里取了个名字  7167913878           男            96   \n",
       "862           0      东湖子非君  7256727188           男           233   \n",
       "863           0      WZQ翌翔  5621168251           男            56   \n",
       "864           0    6666Zyc  7714551751           男           125   \n",
       "865           0      通海乐梅人  1793857883           男            24   \n",
       "\n",
       "    followers_count source  积极词数  消极词数  情感倾向得分   简单情感标签 SnowNLP情感标签    最终情感标签  \n",
       "0                 0   来自广东     0     0       0  neutral    positive  positive  \n",
       "1                 0   来自广东     0     0       0  neutral    positive  positive  \n",
       "2                 3   来自广东     0     0       0  neutral     neutral   neutral  \n",
       "3                 0   来自广东     0     0       0  neutral     neutral   neutral  \n",
       "4                 0   来自广东     0     0       0  neutral     neutral   neutral  \n",
       "..              ...    ...   ...   ...     ...      ...         ...       ...  \n",
       "861              35   来自安徽     0     0       0  neutral     neutral   neutral  \n",
       "862              64   来自安徽     0     0       0  neutral     neutral   neutral  \n",
       "863              23   来自安徽     0     0       0  neutral    positive  positive  \n",
       "864              10   来自安徽     0     0       0  neutral     neutral   neutral  \n",
       "865              75   来自安徽     0     0       0  neutral     neutral   neutral  \n",
       "\n",
       "[866 rows x 17 columns]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "4a9ad7c8-fe32-4028-8e57-618438621ce3",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Building prefix dict from the default dictionary ...\n",
      "Loading model from cache C:\\Users\\17828\\AppData\\Local\\Temp\\jieba.cache\n",
      "Loading model cost 1.749 seconds.\n",
      "Prefix dict has been built successfully.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       0.40      0.21      0.28        28\n",
      "           1       0.59      0.82      0.69        67\n",
      "           2       0.83      0.70      0.76        79\n",
      "\n",
      "    accuracy                           0.67       174\n",
      "   macro avg       0.61      0.58      0.58       174\n",
      "weighted avg       0.67      0.67      0.65       174\n",
      "\n",
      "1. 特征 1062 重要性: 0.10046990682889517\n",
      "2. 特征 410 重要性: 0.07950246618045398\n",
      "3. 特征 334 重要性: 0.045254335441617916\n",
      "4. 特征 415 重要性: 0.03654672766055117\n",
      "5. 特征 88 重要性: 0.0207152064017588\n",
      "6. 特征 320 重要性: 0.020682225016321075\n",
      "7. 特征 750 重要性: 0.013391248648079257\n",
      "8. 特征 321 重要性: 0.01096129488770178\n",
      "9. 特征 526 重要性: 0.010201183365522878\n",
      "10. 特征 1061 重要性: 0.008727374923050743\n"
     ]
    }
   ],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.feature_extraction.text import TfidfVectorizer\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.metrics import classification_report\n",
    "import jieba  # 用于中文分词\n",
    "import re  # 用于正则表达式操作\n",
    "\n",
    "\n",
    "\n",
    "# 数据预处理\n",
    "# 确保'text'列中的所有值都是字符串类型，并填充缺失值\n",
    "data['text'] = data['text'].fillna('').astype(str)\n",
    "\n",
    "# 预处理'followers_count'列，转换包含“万”的值为具体数字\n",
    "def convert_followers_count(value):\n",
    "    if isinstance(value, int):\n",
    "        # 如果已经是整数，则直接返回（或者根据需要进行其他转换）\n",
    "        return value\n",
    "    elif isinstance(value, str):\n",
    "        if '万' in value:\n",
    "            # 提取数字部分并转换为浮点数，然后乘以10000\n",
    "            return float(re.search(r'\\d+\\.?\\d*', value).group()) * 10000\n",
    "        else:\n",
    "            # 否则，尝试直接转换为浮点数（如果已经是数字字符串）\n",
    "            return float(value) if value.replace('.', '', 1).isdigit() else 0  # 处理可能的非数字值\n",
    "    else:\n",
    "        # 对于其他类型，返回0（或者根据需要进行其他处理）\n",
    "        return 0\n",
    "\n",
    "# 应用转换函数到'followers_count'列，并确保结果为整数类型\n",
    "data['followers_count'] = data['followers_count'].apply(convert_followers_count).astype(int)\n",
    "\n",
    "# 确保'follow_count'列中的数据都是数值类型（如果不需要处理“万”单位，则直接转换）\n",
    "data['follow_count'] = pd.to_numeric(data['follow_count'], errors='coerce').fillna(0).astype(int)\n",
    "\n",
    "# 文本清洗函数\n",
    "def clean_text(text):\n",
    "    if not isinstance(text, str):\n",
    "        text = str(text)  # 确保输入是字符串类型\n",
    "    text = re.sub(r'[^\\w\\s]', '', text)  # 去除特殊符号\n",
    "    text = re.sub(r'\\d+', '', text)  # 去除数字\n",
    "    words = jieba.cut(text)  # 中文分词\n",
    "    text = ' '.join(words)\n",
    "    return text\n",
    "\n",
    "data['cleaned_text'] = data['text'].apply(clean_text)\n",
    "\n",
    "# 构造用户影响力特征\n",
    "data['user_influence'] = data['followers_count'] / (data['follow_count'] + 1)  # 避免除以0\n",
    "\n",
    "# 转换用户性别为数值型\n",
    "data['user_gender'] = data['user_gender'].map({'男': 0, '女': 1})\n",
    "\n",
    "# 提取话题标签（简单示例，实际中需要更复杂的逻辑）\n",
    "def extract_hashtags(text):\n",
    "    hashtags = re.findall(r'#\\w+#', text)\n",
    "    return ' '.join(hashtags)\n",
    "\n",
    "data['hashtags'] = data['text'].apply(extract_hashtags)\n",
    "\n",
    "# 特征构造\n",
    "# TF-IDF文本向量\n",
    "tfidf_vectorizer = TfidfVectorizer(max_features=5000)\n",
    "tfidf_matrix = tfidf_vectorizer.fit_transform(data['cleaned_text'])\n",
    "\n",
    "# 用户特征\n",
    "user_features = data[['user_gender', 'user_influence']]\n",
    "\n",
    "# 合并特征\n",
    "X = np.hstack((tfidf_matrix.toarray(), user_features))\n",
    "\n",
    "# 标签（以最终情感标签为例，转换为数值型）\n",
    "label_mapping = {'negative': 0, 'neutral': 1, 'positive': 2}\n",
    "y = data['最终情感标签'].map(label_mapping)\n",
    "\n",
    "# 划分训练集和测试集\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
    "\n",
    "# 数据标准化（仅对用户特征进行标准化，TF-IDF特征通常不需要）\n",
    "scaler = StandardScaler()\n",
    "user_features_train = X_train[:, -user_features.shape[1]:]\n",
    "user_features_test = X_test[:, -user_features.shape[1]:]\n",
    "user_features_train_scaled = scaler.fit_transform(user_features_train)\n",
    "user_features_test_scaled = scaler.transform(user_features_test)\n",
    "\n",
    "# 合并标准化后的用户特征和TF-IDF特征\n",
    "X_train_scaled = np.hstack((X_train[:, :-user_features.shape[1]], user_features_train_scaled))\n",
    "X_test_scaled = np.hstack((X_test[:, :-user_features.shape[1]], user_features_test_scaled))\n",
    "\n",
    "# 模型构建（以随机森林为例）\n",
    "clf = RandomForestClassifier(n_estimators=100, random_state=42)\n",
    "clf.fit(X_train_scaled, y_train)\n",
    "\n",
    "# 预测与评估\n",
    "y_pred = clf.predict(X_test_scaled)\n",
    "print(classification_report(y_test, y_pred))\n",
    "\n",
    "# 特征重要性分析\n",
    "importances = clf.feature_importances_\n",
    "indices = np.argsort(importances)[::-1]\n",
    "top_n = 10  # 显示前10个重要特征\n",
    "for i in range(top_n):\n",
    "    print(f\"{i + 1}. 特征 {indices[i]} 重要性: {importances[indices[i]]}\")\n",
    "    # 注意：这里的特征索引需要结合TF-IDF和用户特征的实际位置来解释"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4f729c3c-8f78-4474-bac7-fbe132afd920",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import jieba\n",
    "import re\n",
    "import spacy\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.feature_extraction.text import TfidfVectorizer\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from xgboost import XGBClassifier\n",
    "from sklearn.metrics import classification_report\n",
    "\n",
    "# 加载spaCy中文模型（首次运行需先执行：python -m spacy download zh_core_web_sm）\n",
    "try:\n",
    "    nlp = spacy.load(\"zh_core_web_sm\")\n",
    "except OSError:\n",
    "    print(\"错误：未找到spaCy中文模型，请先执行：python -m spacy download zh_core_web_sm\")\n",
    "    exit()\n",
    "\n",
    "# 完整数据处理流程\n",
    "def full_preprocessing(data):\n",
    "    # 1. 基础数据清洗\n",
    "    data['text'] = data['text'].fillna('').astype(str)\n",
    "    data['followers_count'] = data['followers_count'].apply(\n",
    "        lambda x: float(re.search(r'\\d+\\.?\\d*', str(x)).group())*10000 if '万' in str(x) else int(x)\n",
    "    ).astype(int)\n",
    "    data['follow_count'] = pd.to_numeric(data['follow_count'], errors='coerce').fillna(0).astype(int)\n",
    "    \n",
    "    # 2. 时间特征提取\n",
    "    data['created_at'] = pd.to_datetime(data['created_at'])\n",
    "    data['hour'] = data['created_at'].dt.hour\n",
    "    data['day_of_week'] = data['created_at'].dt.dayofweek\n",
    "    \n",
    "    # 3. 文本预处理函数\n",
    "    def preprocess_text(text):\n",
    "        # 基础清洗\n",
    "        text = re.sub(r'[^\\w\\s]', '', text)\n",
    "        text = re.sub(r'\\d+', '', text)\n",
    "        words = jieba.cut(text)\n",
    "        cleaned_text = ' '.join(words)\n",
    "        \n",
    "        # 句法分析\n",
    "        doc = nlp(cleaned_text)\n",
    "        syntax_features = {\n",
    "            'verb_count': 0,\n",
    "            'noun_count': 0,\n",
    "            'adj_count': 0,\n",
    "            'adv_count': 0,\n",
    "            'prep_count': 0,\n",
    "            'conj_count': 0,\n",
    "            'avg_dep_distance': 0\n",
    "        }\n",
    "        \n",
    "        total_words = 0\n",
    "        total_distance = 0\n",
    "        \n",
    "        for token in doc:\n",
    "            # 词性统计\n",
    "            if token.pos_ == \"VERB\":\n",
    "                syntax_features['verb_count'] += 1\n",
    "            elif token.pos_ == \"NOUN\":\n",
    "                syntax_features['noun_count'] += 1\n",
    "            elif token.pos_ == \"ADJ\":\n",
    "                syntax_features['adj_count'] += 1\n",
    "            elif token.pos_ == \"ADV\":\n",
    "                syntax_features['adv_count'] += 1\n",
    "            elif token.pos_ == \"ADP\":\n",
    "                syntax_features['prep_count'] += 1\n",
    "            elif token.pos_ in [\"CCONJ\", \"SCONJ\"]:\n",
    "                syntax_features['conj_count'] += 1\n",
    "            \n",
    "            # 依存距离计算\n",
    "            if token.head != token and token.head.i != -1:\n",
    "                total_distance += abs(token.i - token.head.i)\n",
    "                total_words += 1\n",
    "        \n",
    "        if total_words > 0:\n",
    "            syntax_features['avg_dep_distance'] = total_distance / total_words\n",
    "        \n",
    "        return cleaned_text, syntax_features\n",
    "\n",
    "    # 应用预处理\n",
    "    cleaned_texts = []\n",
    "    syntax_features_df = pd.DataFrame()\n",
    "    \n",
    "    for text in data['text']:\n",
    "        cleaned_text, features = preprocess_text(text)\n",
    "        cleaned_texts.append(cleaned_text)\n",
    "        syntax_features_df = syntax_features_df.append(features, ignore_index=True)\n",
    "    \n",
    "    # 合并所有特征\n",
    "    data = pd.concat([\n",
    "        data,\n",
    "        syntax_features_df,\n",
    "        pd.get_dummies(data['最终情感标签'], prefix='sentiment')  # 标签编码\n",
    "    ], axis=1)\n",
    "    \n",
    "    data['cleaned_text'] = cleaned_texts\n",
    "    data['user_influence'] = data['followers_count'] / (data['follow_count'] + 1)\n",
    "    data['user_gender'] = data['user_gender'].map({'男': 0, '女': 1})\n",
    "    \n",
    "    return data\n",
    "\n",
    "# 执行完整预处理流程\n",
    "data = full_preprocessing(pd.read_csv('weibo_comments.csv'))\n",
    "\n",
    "# 特征工程\n",
    "# 1. 文本特征\n",
    "tfidf = TfidfVectorizer(max_features=5000)\n",
    "text_features = tfidf.fit_transform(data['cleaned_text'])\n",
    "\n",
    "# 2. 句法特征\n",
    "syntax_features = data[[\n",
    "    'verb_count', 'noun_count', 'adj_count', \n",
    "    'adv_count', 'prep_count', 'conj_count', \n",
    "    'avg_dep_distance'\n",
    "]]\n",
    "\n",
    "# 3. 用户特征\n",
    "user_features = data[['user_gender', 'user_influence', 'hour', 'day_of_week']]\n",
    "\n",
    "# 合并所有特征\n",
    "X = np.hstack((\n",
    "    text_features.toarray(),\n",
    "    syntax_features,\n",
    "    user_features\n",
    "))\n",
    "\n",
    "# 标签\n",
    "y = data['sentiment_positive']  # 使用positive类别作为示例标签\n",
    "\n",
    "# 划分数据集\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
    "\n",
    "# 特征标准化\n",
    "scaler = StandardScaler()\n",
    "numeric_features = np.hstack((syntax_features, user_features))\n",
    "scaled_features = scaler.fit_transform(numeric_features)\n",
    "\n",
    "# 重组特征矩阵\n",
    "X_train_scaled = np.hstack((X_train[:, :text_features.shape[1]], scaled_features[:X_train.shape[0]]))\n",
    "X_test_scaled = np.hstack((X_test[:, :text_features.shape[1]], scaled_features[X_train.shape[0]:]))\n",
    "\n",
    "# 模型训练\n",
    "model = XGBClassifier(\n",
    "    n_estimators=300,\n",
    "    max_depth=6,\n",
    "    learning_rate=0.05,\n",
    "    subsample=0.8,\n",
    "    colsample_bytree=0.8,\n",
    "    random_state=42\n",
    ")\n",
    "model.fit(X_train_scaled, y_train)\n",
    "\n",
    "# 模型评估\n",
    "y_pred = model.predict(X_test_scaled)\n",
    "print(classification_report(y_test, y_pred))\n",
    "\n",
    "# 特征重要性分析\n",
    "importances = model.feature_importances_\n",
    "indices = np.argsort(importances)[::-1]\n",
    "print(\"重要特征展示：\")\n",
    "for i in range(20):\n",
    "    print(f\"{i+1}. 特征 {indices[i]} 重要性: {importances[indices[i]]:.4f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b67d0abb-65b9-4d6c-991a-3bbc364aace2",
   "metadata": {},
   "outputs": [],
   "source": [
    "pip install xgboost"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "4cb2434b-5a3b-4b0b-8a18-b22632ae5efe",
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "invalid syntax (2801314746.py, line 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;36m  Cell \u001b[1;32mIn[1], line 1\u001b[1;36m\u001b[0m\n\u001b[1;33m    python -m spacy download zh_core_web_sm\u001b[0m\n\u001b[1;37m              ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n"
     ]
    }
   ],
   "source": [
    "python -m spacy download zh_core_web_sm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "2373c0d0-903f-49d3-82a9-efef222faaf5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "错误：未找到spaCy中文模型，请先执行：python -m spacy download zh_core_web_sm\n"
     ]
    },
    {
     "ename": "KeyError",
     "evalue": "\"None of [Index(['verb_count', 'noun_count', 'adj_count', 'adv_count', 'prep_count',\\n       'conj_count', 'avg_dep_distance'],\\n      dtype='object')] are in the [columns]\"",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mKeyError\u001b[0m                                  Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[7], line 111\u001b[0m\n\u001b[0;32m    108\u001b[0m text_features \u001b[38;5;241m=\u001b[39m tfidf\u001b[38;5;241m.\u001b[39mfit_transform(data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcleaned_text\u001b[39m\u001b[38;5;124m'\u001b[39m])\n\u001b[0;32m    110\u001b[0m \u001b[38;5;66;03m# 2. 句法特征\u001b[39;00m\n\u001b[1;32m--> 111\u001b[0m syntax_features \u001b[38;5;241m=\u001b[39m data[[\n\u001b[0;32m    112\u001b[0m     \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mverb_count\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnoun_count\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124madj_count\u001b[39m\u001b[38;5;124m'\u001b[39m, \n\u001b[0;32m    113\u001b[0m     \u001b[38;5;124m'\u001b[39m\u001b[38;5;124madv_count\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mprep_count\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mconj_count\u001b[39m\u001b[38;5;124m'\u001b[39m, \n\u001b[0;32m    114\u001b[0m     \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mavg_dep_distance\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m    115\u001b[0m ]]\n\u001b[0;32m    117\u001b[0m \u001b[38;5;66;03m# 3. 用户特征\u001b[39;00m\n\u001b[0;32m    118\u001b[0m user_features \u001b[38;5;241m=\u001b[39m data[[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124muser_gender\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124muser_influence\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mhour\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mday_of_week\u001b[39m\u001b[38;5;124m'\u001b[39m]]\n",
      "File \u001b[1;32mC:\\ProgramData\\anaconda3\\Lib\\site-packages\\pandas\\core\\frame.py:4108\u001b[0m, in \u001b[0;36mDataFrame.__getitem__\u001b[1;34m(self, key)\u001b[0m\n\u001b[0;32m   4106\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m is_iterator(key):\n\u001b[0;32m   4107\u001b[0m         key \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(key)\n\u001b[1;32m-> 4108\u001b[0m     indexer \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcolumns\u001b[38;5;241m.\u001b[39m_get_indexer_strict(key, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcolumns\u001b[39m\u001b[38;5;124m\"\u001b[39m)[\u001b[38;5;241m1\u001b[39m]\n\u001b[0;32m   4110\u001b[0m \u001b[38;5;66;03m# take() does not accept boolean indexers\u001b[39;00m\n\u001b[0;32m   4111\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mgetattr\u001b[39m(indexer, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdtype\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m) \u001b[38;5;241m==\u001b[39m \u001b[38;5;28mbool\u001b[39m:\n",
      "File \u001b[1;32mC:\\ProgramData\\anaconda3\\Lib\\site-packages\\pandas\\core\\indexes\\base.py:6200\u001b[0m, in \u001b[0;36mIndex._get_indexer_strict\u001b[1;34m(self, key, axis_name)\u001b[0m\n\u001b[0;32m   6197\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m   6198\u001b[0m     keyarr, indexer, new_indexer \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_reindex_non_unique(keyarr)\n\u001b[1;32m-> 6200\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_raise_if_missing(keyarr, indexer, axis_name)\n\u001b[0;32m   6202\u001b[0m keyarr \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtake(indexer)\n\u001b[0;32m   6203\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(key, Index):\n\u001b[0;32m   6204\u001b[0m     \u001b[38;5;66;03m# GH 42790 - Preserve name from an Index\u001b[39;00m\n",
      "File \u001b[1;32mC:\\ProgramData\\anaconda3\\Lib\\site-packages\\pandas\\core\\indexes\\base.py:6249\u001b[0m, in \u001b[0;36mIndex._raise_if_missing\u001b[1;34m(self, key, indexer, axis_name)\u001b[0m\n\u001b[0;32m   6247\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m nmissing:\n\u001b[0;32m   6248\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m nmissing \u001b[38;5;241m==\u001b[39m \u001b[38;5;28mlen\u001b[39m(indexer):\n\u001b[1;32m-> 6249\u001b[0m         \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNone of [\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mkey\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m] are in the [\u001b[39m\u001b[38;5;132;01m{\u001b[39;00maxis_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m]\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m   6251\u001b[0m     not_found \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(ensure_index(key)[missing_mask\u001b[38;5;241m.\u001b[39mnonzero()[\u001b[38;5;241m0\u001b[39m]]\u001b[38;5;241m.\u001b[39munique())\n\u001b[0;32m   6252\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mnot_found\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m not in index\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
      "\u001b[1;31mKeyError\u001b[0m: \"None of [Index(['verb_count', 'noun_count', 'adj_count', 'adv_count', 'prep_count',\\n       'conj_count', 'avg_dep_distance'],\\n      dtype='object')] are in the [columns]\""
     ]
    }
   ],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import jieba\n",
    "import re\n",
    "import spacy\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.feature_extraction.text import TfidfVectorizer\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from sklearn.ensemble import RandomForestClassifier  # 修改为随机森林\n",
    "from sklearn.metrics import classification_report\n",
    "\n",
    "# 加载spaCy中文模型（首次运行需先执行：python -m spacy download zh_core_web_sm）\n",
    "try:\n",
    "    nlp = spacy.load(\"zh_core_web_sm\")\n",
    "except OSError:\n",
    "    print(\"错误：未找到spaCy中文模型，请先执行：python -m spacy download zh_core_web_sm\")\n",
    "    exit()\n",
    "\n",
    "# 完整数据处理流程（保持不变）\n",
    "def full_preprocessing(data):\n",
    "    # 1. 基础数据清洗\n",
    "    data['text'] = data['text'].fillna('').astype(str)\n",
    "    data['followers_count'] = data['followers_count'].apply(\n",
    "        lambda x: float(re.search(r'\\d+\\.?\\d*', str(x)).group())*10000 if '万' in str(x) else int(x)\n",
    "    ).astype(int)\n",
    "    data['follow_count'] = pd.to_numeric(data['follow_count'], errors='coerce').fillna(0).astype(int)\n",
    "    \n",
    "    # 2. 时间特征提取\n",
    "    data['created_at'] = pd.to_datetime(data['created_at'])\n",
    "    data['hour'] = data['created_at'].dt.hour\n",
    "    data['day_of_week'] = data['created_at'].dt.dayofweek\n",
    "    \n",
    "    # 3. 文本预处理函数\n",
    "    def preprocess_text(text):\n",
    "        # 基础清洗\n",
    "        text = re.sub(r'[^\\w\\s]', '', text)\n",
    "        text = re.sub(r'\\d+', '', text)\n",
    "        words = jieba.cut(text)\n",
    "        cleaned_text = ' '.join(words)\n",
    "        \n",
    "        # 句法分析\n",
    "        doc = nlp(cleaned_text)\n",
    "        syntax_features = {\n",
    "            'verb_count': 0,\n",
    "            'noun_count': 0,\n",
    "            'adj_count': 0,\n",
    "            'adv_count': 0,\n",
    "            'prep_count': 0,\n",
    "            'conj_count': 0,\n",
    "            'avg_dep_distance': 0\n",
    "        }\n",
    "        \n",
    "        total_words = 0\n",
    "        total_distance = 0\n",
    "        \n",
    "        for token in doc:\n",
    "            # 词性统计\n",
    "            if token.pos_ == \"VERB\":\n",
    "                syntax_features['verb_count'] += 1\n",
    "            elif token.pos_ == \"NOUN\":\n",
    "                syntax_features['noun_count'] += 1\n",
    "            elif token.pos_ == \"ADJ\":\n",
    "                syntax_features['adj_count'] += 1\n",
    "            elif token.pos_ == \"ADV\":\n",
    "                syntax_features['adv_count'] += 1\n",
    "            elif token.pos_ == \"ADP\":\n",
    "                syntax_features['prep_count'] += 1\n",
    "            elif token.pos_ in [\"CCONJ\", \"SCONJ\"]:\n",
    "                syntax_features['conj_count'] += 1\n",
    "            \n",
    "            # 依存距离计算\n",
    "            if token.head != token and token.head.i != -1:\n",
    "                total_distance += abs(token.i - token.head.i)\n",
    "                total_words += 1\n",
    "        \n",
    "        if total_words > 0:\n",
    "            syntax_features['avg_dep_distance'] = total_distance / total_words\n",
    "        \n",
    "        return cleaned_text, syntax_features\n",
    "\n",
    "    # 应用预处理\n",
    "    cleaned_texts = []\n",
    "    syntax_features_df = pd.DataFrame()\n",
    "    \n",
    "    for text in data['text']:\n",
    "        cleaned_text, features = preprocess_text(text)\n",
    "        cleaned_texts.append(cleaned_text)\n",
    "        syntax_features_df = syntax_features_df.append(features, ignore_index=True)\n",
    "    \n",
    "    # 合并所有特征\n",
    "    data = pd.concat([\n",
    "        data,\n",
    "        syntax_features_df,\n",
    "        pd.get_dummies(data['最终情感标签'], prefix='sentiment')  # 标签编码\n",
    "    ], axis=1)\n",
    "    \n",
    "    data['cleaned_text'] = cleaned_texts\n",
    "    data['user_influence'] = data['followers_count'] / (data['follow_count'] + 1)\n",
    "    data['user_gender'] = data['user_gender'].map({'男': 0, '女': 1})\n",
    "    \n",
    "    return data\n",
    "\n",
    "\n",
    "\n",
    "# 特征工程（保持不变）\n",
    "# 1. 文本特征\n",
    "tfidf = TfidfVectorizer(max_features=5000)\n",
    "text_features = tfidf.fit_transform(data['cleaned_text'])\n",
    "\n",
    "# 2. 句法特征\n",
    "syntax_features = data[[\n",
    "    'verb_count', 'noun_count', 'adj_count', \n",
    "    'adv_count', 'prep_count', 'conj_count', \n",
    "    'avg_dep_distance'\n",
    "]]\n",
    "\n",
    "# 3. 用户特征\n",
    "user_features = data[['user_gender', 'user_influence', 'hour', 'day_of_week']]\n",
    "\n",
    "# 合并所有特征\n",
    "X = np.hstack((\n",
    "    text_features.toarray(),\n",
    "    syntax_features,\n",
    "    user_features\n",
    "))\n",
    "\n",
    "# 标签\n",
    "y = data['sentiment_positive']  # 使用positive类别作为示例标签\n",
    "\n",
    "# 划分数据集（保持不变）\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
    "\n",
    "# 特征标准化（随机森林通常不需要标准化，但为保持流程一致性暂不修改）\n",
    "scaler = StandardScaler()\n",
    "numeric_features = np.hstack((syntax_features, user_features))\n",
    "scaled_features = scaler.fit_transform(numeric_features)\n",
    "\n",
    "# 重组特征矩阵\n",
    "X_train_scaled = np.hstack((X_train[:, :text_features.shape[1]], scaled_features[:X_train.shape[0]]))\n",
    "X_test_scaled = np.hstack((X_test[:, :text_features.shape[1]], scaled_features[X_train.shape[0]:]))\n",
    "\n",
    "# 模型训练（修改为随机森林）\n",
    "model = RandomForestClassifier(\n",
    "    n_estimators=300,  # 树的数量\n",
    "    max_depth=8,       # 树的最大深度\n",
    "    min_samples_split=5,  # 节点分裂最小样本数\n",
    "    random_state=42,\n",
    "    n_jobs=-1          # 使用所有CPU核心\n",
    ")\n",
    "model.fit(X_train_scaled, y_train)\n",
    "\n",
    "# 模型评估（保持不变）\n",
    "y_pred = model.predict(X_test_scaled)\n",
    "print(classification_report(y_test, y_pred))\n",
    "\n",
    "# 特征重要性分析（保持不变）\n",
    "importances = model.feature_importances_\n",
    "indices = np.argsort(importances)[::-1]\n",
    "print(\"重要特征展示：\")\n",
    "for i in range(20):\n",
    "    print(f\"{i+1}. 特征 {indices[i]} 重要性: {importances[indices[i]]:.4f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "107c7bd3-a3c9-4e7f-b42e-1b7bc66d9724",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "cfdd1957-a8b4-443a-b2c8-4cea6c53cd85",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Building prefix dict from the default dictionary ...\n",
      "Loading model from cache C:\\Users\\17828\\AppData\\Local\\Temp\\jieba.cache\n",
      "Loading model cost 1.730 seconds.\n",
      "Prefix dict has been built successfully.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "分类报告：\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       0.79      0.70      0.74        79\n",
      "           1       0.58      0.85      0.69        67\n",
      "           2       0.60      0.11      0.18        28\n",
      "\n",
      "    accuracy                           0.66       174\n",
      "   macro avg       0.65      0.55      0.54       174\n",
      "weighted avg       0.67      0.66      0.63       174\n",
      "\n"
     ]
    },
    {
     "ename": "OSError",
     "evalue": "[E050] Can't find model 'zh_core_web_sm'. It doesn't seem to be a Python package or a valid path to a data directory.",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mOSError\u001b[0m                                   Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[3], line 58\u001b[0m\n\u001b[0;32m     54\u001b[0m \u001b[38;5;28mprint\u001b[39m(classification_report(y_test, y_pred))\n\u001b[0;32m     56\u001b[0m \u001b[38;5;66;03m# 3. 句法分析\u001b[39;00m\n\u001b[0;32m     57\u001b[0m \u001b[38;5;66;03m# 加载spacy中文模型（需先运行：python -m spacy download zh_core_web_sm）\u001b[39;00m\n\u001b[1;32m---> 58\u001b[0m nlp \u001b[38;5;241m=\u001b[39m spacy\u001b[38;5;241m.\u001b[39mload(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mzh_core_web_sm\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m     60\u001b[0m \u001b[38;5;66;03m# 依存分析示例\u001b[39;00m\n\u001b[0;32m     61\u001b[0m sample_text \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m奶茶虽然好喝但价格太贵了\u001b[39m\u001b[38;5;124m\"\u001b[39m\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\spacy\\__init__.py:51\u001b[0m, in \u001b[0;36mload\u001b[1;34m(name, vocab, disable, enable, exclude, config)\u001b[0m\n\u001b[0;32m     27\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mload\u001b[39m(\n\u001b[0;32m     28\u001b[0m     name: Union[\u001b[38;5;28mstr\u001b[39m, Path],\n\u001b[0;32m     29\u001b[0m     \u001b[38;5;241m*\u001b[39m,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m     34\u001b[0m     config: Union[Dict[\u001b[38;5;28mstr\u001b[39m, Any], Config] \u001b[38;5;241m=\u001b[39m util\u001b[38;5;241m.\u001b[39mSimpleFrozenDict(),\n\u001b[0;32m     35\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Language:\n\u001b[0;32m     36\u001b[0m \u001b[38;5;250m    \u001b[39m\u001b[38;5;124;03m\"\"\"Load a spaCy model from an installed package or a local path.\u001b[39;00m\n\u001b[0;32m     37\u001b[0m \n\u001b[0;32m     38\u001b[0m \u001b[38;5;124;03m    name (str): Package name or model path.\u001b[39;00m\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m     49\u001b[0m \u001b[38;5;124;03m    RETURNS (Language): The loaded nlp object.\u001b[39;00m\n\u001b[0;32m     50\u001b[0m \u001b[38;5;124;03m    \"\"\"\u001b[39;00m\n\u001b[1;32m---> 51\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m util\u001b[38;5;241m.\u001b[39mload_model(\n\u001b[0;32m     52\u001b[0m         name,\n\u001b[0;32m     53\u001b[0m         vocab\u001b[38;5;241m=\u001b[39mvocab,\n\u001b[0;32m     54\u001b[0m         disable\u001b[38;5;241m=\u001b[39mdisable,\n\u001b[0;32m     55\u001b[0m         enable\u001b[38;5;241m=\u001b[39menable,\n\u001b[0;32m     56\u001b[0m         exclude\u001b[38;5;241m=\u001b[39mexclude,\n\u001b[0;32m     57\u001b[0m         config\u001b[38;5;241m=\u001b[39mconfig,\n\u001b[0;32m     58\u001b[0m     )\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\spacy\\util.py:472\u001b[0m, in \u001b[0;36mload_model\u001b[1;34m(name, vocab, disable, enable, exclude, config)\u001b[0m\n\u001b[0;32m    470\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m name \u001b[38;5;129;01min\u001b[39;00m OLD_MODEL_SHORTCUTS:\n\u001b[0;32m    471\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mIOError\u001b[39;00m(Errors\u001b[38;5;241m.\u001b[39mE941\u001b[38;5;241m.\u001b[39mformat(name\u001b[38;5;241m=\u001b[39mname, full\u001b[38;5;241m=\u001b[39mOLD_MODEL_SHORTCUTS[name]))  \u001b[38;5;66;03m# type: ignore[index]\u001b[39;00m\n\u001b[1;32m--> 472\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mIOError\u001b[39;00m(Errors\u001b[38;5;241m.\u001b[39mE050\u001b[38;5;241m.\u001b[39mformat(name\u001b[38;5;241m=\u001b[39mname))\n",
      "\u001b[1;31mOSError\u001b[0m: [E050] Can't find model 'zh_core_web_sm'. It doesn't seem to be a Python package or a valid path to a data directory."
     ]
    }
   ],
   "source": [
    "# -*- coding: utf-8 -*-\n",
    "# @Author: AI悦创\n",
    "# @Date: 2023-08-18 17:14:21\n",
    "# @LastEditors: AI悦创\n",
    "# @LastEditTime: 2023-08-18 17:55:23\n",
    "# @FilePath: \\code\\奶茶评论分析.py\n",
    "# @Description: \n",
    "\n",
    "import jieba\n",
    "import spacy\n",
    "from spacy import displacy\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.feature_extraction.text import TfidfVectorizer\n",
    "from sklearn.svm import SVC\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.metrics import classification_report, confusion_matrix, roc_curve, auc\n",
    "from sklearn.preprocessing import label_binarize\n",
    "import seaborn as sns\n",
    "\n",
    "# 1. 数据预处理\n",
    "def preprocess_text(text):\n",
    "    # 加载停用词表（需准备中文停用词文件）\n",
    "    with open('chinese_stopwords.txt', encoding='utf-8') as f:\n",
    "        stopwords = set(f.read().split())\n",
    "    \n",
    "    words = jieba.cut(text)\n",
    "    return ' '.join([word for word in words if word not in stopwords and len(word) > 1])\n",
    "\n",
    "# 加载数据\n",
    "df = pd.read_csv('final_labeled_comments.csv')\n",
    "df['processed_text'] = df['text'].astype(str).apply(preprocess_text)\n",
    "\n",
    "# 2. 文本分类（情感分析）\n",
    "# 标签编码\n",
    "label_map = {'positive': 0, 'neutral': 1, 'negative': 2}\n",
    "df['label'] = df['最终情感标签'].map(label_map)\n",
    "\n",
    "# TF-IDF向量化\n",
    "tfidf = TfidfVectorizer(max_features=2000)\n",
    "X = tfidf.fit_transform(df['processed_text'])\n",
    "y = df['label']\n",
    "\n",
    "# 拆分数据集\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
    "\n",
    "# 训练SVM分类器\n",
    "clf = SVC(probability=True, kernel='linear')\n",
    "clf.fit(X_train, y_train)\n",
    "\n",
    "# 评估模型\n",
    "y_pred = clf.predict(X_test)\n",
    "print(\"分类报告：\")\n",
    "print(classification_report(y_test, y_pred))\n",
    "\n",
    "# 3. 句法分析\n",
    "# 加载spacy中文模型（需先运行：python -m spacy download zh_core_web_sm）\n",
    "nlp = spacy.load(\"zh_core_web_sm\")\n",
    "\n",
    "# 依存分析示例\n",
    "sample_text = \"奶茶虽然好喝但价格太贵了\"\n",
    "doc = nlp(sample_text)\n",
    "\n",
    "# 生成依存关系可视化\n",
    "displacy.render(doc, style=\"dep\", jupyter=False).save(\"dep_plot.html\")\n",
    "\n",
    "# 4. 可视化分析\n",
    "# 混淆矩阵\n",
    "cm = confusion_matrix(y_test, y_pred)\n",
    "plt.figure(figsize=(8,6))\n",
    "sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',\n",
    "            xticklabels=label_map.keys(),\n",
    "            yticklabels=label_map.keys())\n",
    "plt.title('Confusion Matrix')\n",
    "plt.savefig('confusion_matrix.png')\n",
    "\n",
    "# ROC曲线（多分类处理）\n",
    "y_test_bin = label_binarize(y_test, classes=[0,1,2])\n",
    "y_pred_prob = clf.predict_proba(X_test)\n",
    "\n",
    "plt.figure(figsize=(10,8))\n",
    "for i in range(3):\n",
    "    fpr, tpr, _ = roc_curve(y_test_bin[:,i], y_pred_prob[:,i])\n",
    "    roc_auc = auc(fpr, tpr)\n",
    "    plt.plot(fpr, tpr, label=f'Class {list(label_map.keys())[i]} (AUC = {roc_auc:.2f})')\n",
    "\n",
    "plt.plot([0,1], [0,1], 'k--')\n",
    "plt.xlabel('False Positive Rate')\n",
    "plt.ylabel('True Positive Rate')\n",
    "plt.title('Multiclass ROC Curve')\n",
    "plt.legend()\n",
    "plt.savefig('roc_curve.png')\n",
    "\n",
    "# 5. 舆情洞见分析\n",
    "def extract_opinions(text):\n",
    "    doc = nlp(text)\n",
    "    opinions = []\n",
    "    for token in doc:\n",
    "        # 提取形容词+名词组合\n",
    "        if token.pos_ == 'ADJ' and token.head.pos_ == 'NOUN':\n",
    "            opinions.append(f\"{token.head.text}->{token.text}\")\n",
    "    return opinions\n",
    "\n",
    "# 应用语义角色分析\n",
    "df['opinions'] = df['text'].astype(str).apply(extract_opinions)\n",
    "positive_opinions = df[df['最终情感标签'] == 'positive']['opinions'].explode().value_counts().head(10)\n",
    "print(\"\\n正向观点Top10：\")\n",
    "print(positive_opinions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "6d6a704d-af10-4231-afb9-498294e31201",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Building prefix dict from the default dictionary ...\n",
      "Loading model from cache C:\\Users\\17828\\AppData\\Local\\Temp\\jieba.cache\n",
      "Loading model cost 1.754 seconds.\n",
      "Prefix dict has been built successfully.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "开始网格搜索...\n",
      "Fitting 5 folds for each of 6561 candidates, totalling 32805 fits\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\17828\\AppData\\Roaming\\Python\\Python312\\site-packages\\xgboost\\training.py:183: UserWarning: [11:12:59] WARNING: C:\\actions-runner\\_work\\xgboost\\xgboost\\src\\learner.cc:738: \n",
      "Parameters: { \"use_label_encoder\" } are not used.\n",
      "\n",
      "  bst.update(dtrain, iteration=i, fobj=obj)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "网格搜索完成!\n",
      "最佳参数组合:  {'colsample_bytree': 0.9, 'gamma': 0.2, 'learning_rate': 0.2, 'max_depth': 5, 'n_estimators': 200, 'reg_alpha': 0, 'reg_lambda': 1, 'subsample': 1.0}\n",
      "最佳准确率:  0.6748514232092587\n",
      "\n",
      "使用最优模型进行评估...\n",
      "分类报告：\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       0.82      0.65      0.72        79\n",
      "           1       0.58      0.88      0.70        67\n",
      "           2       0.50      0.18      0.26        28\n",
      "\n",
      "    accuracy                           0.66       174\n",
      "   macro avg       0.63      0.57      0.56       174\n",
      "weighted avg       0.68      0.66      0.64       174\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\17828\\AppData\\Roaming\\Python\\Python312\\site-packages\\xgboost\\training.py:183: UserWarning: [11:12:59] WARNING: C:\\actions-runner\\_work\\xgboost\\xgboost\\src\\learner.cc:738: \n",
      "Parameters: { \"use_label_encoder\" } are not used.\n",
      "\n",
      "  bst.update(dtrain, iteration=i, fobj=obj)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "最重要的20个特征：\n",
      "    feature  importance\n",
      "5        15    0.053252\n",
      "29      nan    0.049297\n",
      "957      转发    0.040815\n",
      "425      好喝    0.039867\n",
      "420      奶茶    0.032103\n",
      "104      不错    0.031304\n",
      "944      赶紧    0.023978\n",
      "61      七不喝    0.023495\n",
      "332     哈哈哈    0.022042\n",
      "753      爱喝    0.019549\n",
      "100      不能    0.018589\n",
      "113      中午    0.018195\n",
      "315      周一    0.015537\n",
      "534      快乐    0.014497\n",
      "520      微博    0.014117\n",
      "39       一周    0.013287\n",
      "478      小米    0.013269\n",
      "780      生活    0.012543\n",
      "565      我要    0.012447\n",
      "154      今天    0.012028\n"
     ]
    }
   ],
   "source": [
    "# -*- coding: utf-8 -*-\n",
    "import jieba\n",
    "import spacy\n",
    "from spacy import displacy\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.feature_extraction.text import TfidfVectorizer\n",
    "from sklearn.model_selection import train_test_split, GridSearchCV\n",
    "from sklearn.metrics import classification_report, confusion_matrix, roc_curve, auc\n",
    "from sklearn.preprocessing import label_binarize\n",
    "import seaborn as sns\n",
    "import xgboost as xgb\n",
    "from xgboost import XGBClassifier\n",
    "from sklearn.pipeline import Pipeline\n",
    "\n",
    "# 1. 数据预处理\n",
    "def preprocess_text(text):\n",
    "    # 加载停用词表（需准备中文停用词文件）\n",
    "    with open('chinese_stopwords.txt', encoding='utf-8') as f:\n",
    "        stopwords = set(f.read().split())\n",
    "    \n",
    "    words = jieba.cut(text)\n",
    "    return ' '.join([word for word in words if word not in stopwords and len(word) > 1])\n",
    "\n",
    "# 加载数据\n",
    "df = pd.read_csv('final_labeled_comments.csv')\n",
    "df['processed_text'] = df['text'].astype(str).apply(preprocess_text)\n",
    "\n",
    "# 2. 文本分类（情感分析）\n",
    "# 标签编码\n",
    "label_map = {'positive': 0, 'neutral': 1, 'negative': 2}\n",
    "df['label'] = df['最终情感标签'].map(label_map)\n",
    "\n",
    "# TF-IDF向量化\n",
    "tfidf = TfidfVectorizer(max_features=2000)\n",
    "X = tfidf.fit_transform(df['processed_text'])\n",
    "y = df['label']\n",
    "\n",
    "# 拆分数据集\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
    "\n",
    "# 创建XGBoost分类器\n",
    "xgb_clf = XGBClassifier(\n",
    "    objective='multi:softprob',  # 多分类问题\n",
    "    eval_metric='mlogloss',      # 多分类对数损失\n",
    "    use_label_encoder=False,     # 禁用标签编码器警告\n",
    "    random_state=42\n",
    ")\n",
    "\n",
    "# 3. 参数调优\n",
    "def tune_xgboost_params(X_train, y_train):\n",
    "    \"\"\"\n",
    "    使用网格搜索寻找最优参数\n",
    "    \"\"\"\n",
    "    param_grid = {\n",
    "        'max_depth': [3, 5, 7],\n",
    "        'learning_rate': [0.01, 0.1, 0.2],\n",
    "        'n_estimators': [100, 200, 300],\n",
    "        'gamma': [0, 0.1, 0.2],\n",
    "        'subsample': [0.8, 0.9, 1.0],\n",
    "        'colsample_bytree': [0.8, 0.9, 1.0],\n",
    "        'reg_alpha': [0, 0.1, 1],\n",
    "        'reg_lambda': [0.1, 1, 10]\n",
    "    }\n",
    "    \n",
    "    # 创建网格搜索对象\n",
    "    grid_search = GridSearchCV(\n",
    "        estimator=xgb_clf,\n",
    "        param_grid=param_grid,\n",
    "        cv=5,\n",
    "        scoring='accuracy',\n",
    "        n_jobs=-1,\n",
    "        verbose=2\n",
    "    )\n",
    "    \n",
    "    print(\"开始网格搜索...\")\n",
    "    grid_search.fit(X_train, y_train)\n",
    "    print(\"网格搜索完成!\")\n",
    "    \n",
    "    # 输出最佳参数\n",
    "    print(\"最佳参数组合: \", grid_search.best_params_)\n",
    "    print(\"最佳准确率: \", grid_search.best_score_)\n",
    "    \n",
    "    return grid_search.best_estimator_\n",
    "\n",
    "# 调优参数\n",
    "best_xgb = tune_xgboost_params(X_train, y_train)\n",
    "\n",
    "# 4. 使用最优模型进行评估\n",
    "print(\"\\n使用最优模型进行评估...\")\n",
    "y_pred = best_xgb.predict(X_test)\n",
    "print(\"分类报告：\")\n",
    "print(classification_report(y_test, y_pred))\n",
    "\n",
    "# 5. 特征重要性分析\n",
    "# 将稀疏矩阵转换为密集矩阵\n",
    "X_train_dense = X_train.toarray()\n",
    "X_test_dense = X_test.toarray()\n",
    "\n",
    "# 重新训练模型以获取特征重要性\n",
    "best_xgb.fit(X_train_dense, y_train)\n",
    "\n",
    "# 获取特征重要性\n",
    "importance = best_xgb.feature_importances_\n",
    "feature_names = tfidf.get_feature_names_out()\n",
    "\n",
    "# 创建特征重要性DataFrame\n",
    "feature_importance = pd.DataFrame({\n",
    "    'feature': feature_names,\n",
    "    'importance': importance\n",
    "}).sort_values('importance', ascending=False)\n",
    "\n",
    "# 显示最重要的20个特征\n",
    "print(\"\\n最重要的20个特征：\")\n",
    "print(feature_importance.head(20))\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0e25b7e7-ba8b-485d-94ba-b2b2160d3d2d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 6. 句法分析（保持不变）\n",
    "nlp = spacy.load(\"zh_core_web_sm\")\n",
    "sample_text = \"奶茶虽然好喝但价格太贵了\"\n",
    "doc = nlp(sample_text)\n",
    "displacy.render(doc, style=\"dep\", jupyter=False).save(\"dep_plot.html\")\n",
    "\n",
    "# 7. 可视化分析（保持不变）\n",
    "cm = confusion_matrix(y_test, y_pred)\n",
    "plt.figure(figsize=(8,6))\n",
    "sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',\n",
    "            xticklabels=label_map.keys(),\n",
    "            yticklabels=label_map.keys())\n",
    "plt.title('Confusion Matrix')\n",
    "plt.savefig('confusion_matrix.png')\n",
    "\n",
    "# ROC曲线（多分类处理）\n",
    "y_test_bin = label_binarize(y_test, classes=[0,1,2])\n",
    "y_pred_prob = best_xgb.predict_proba(X_test_dense)\n",
    "\n",
    "plt.figure(figsize=(10,8))\n",
    "for i in range(3):\n",
    "    fpr, tpr, _ = roc_curve(y_test_bin[:,i], y_pred_prob[:,i])\n",
    "    roc_auc = auc(fpr, tpr)\n",
    "    plt.plot(fpr, tpr, label=f'Class {list(label_map.keys())[i]} (AUC = {roc_auc:.2f})')\n",
    "\n",
    "plt.plot([0,1], [0,1], 'k--')\n",
    "plt.xlabel('False Positive Rate')\n",
    "plt.ylabel('True Positive Rate')\n",
    "plt.title('Multiclass ROC Curve')\n",
    "plt.legend()\n",
    "plt.savefig('roc_curve.png')\n",
    "\n",
    "# 8. 舆情洞见分析（保持不变）\n",
    "def extract_opinions(text):\n",
    "    doc = nlp(text)\n",
    "    opinions = []\n",
    "    for token in doc:\n",
    "        if token.pos_ == 'ADJ' and token.head.pos_ == 'NOUN':\n",
    "            opinions.append(f\"{token.head.text}->{token.text}\")\n",
    "    return opinions\n",
    "\n",
    "df['opinions'] = df['text'].astype(str).apply(extract_opinions)\n",
    "positive_opinions = df[df['最终情感标签'] == 'positive']['opinions'].explode().value_counts().head(10)\n",
    "print(\"\\n正向观点Top10：\")\n",
    "print(positive_opinions)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9859440-60bb-42ca-a67b-47e39d719278",
   "metadata": {},
   "source": [
    "# 尝试不同的模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4e122a08-b770-496b-8b56-4ad97a574226",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Training Logistic Regression...\n",
      "\n",
      "Training SVM...\n",
      "\n",
      "Training Random Forest...\n",
      "\n",
      "Training Naive Bayes...\n",
      "\n",
      "Training XGBoost...\n",
      "                     accuracy  f1_macro  \\\n",
      "Random Forest        0.672414  0.573697   \n",
      "XGBoost              0.666667  0.553571   \n",
      "SVM                   0.66092  0.535607   \n",
      "Logistic Regression  0.649425  0.526814   \n",
      "Naive Bayes          0.551724  0.423879   \n",
      "\n",
      "                                                                 model  \n",
      "Random Forest        (DecisionTreeClassifier(max_features='sqrt', r...  \n",
      "XGBoost              XGBClassifier(base_score=None, booster=None, c...  \n",
      "SVM                             SVC(kernel='linear', probability=True)  \n",
      "Logistic Regression       LogisticRegression(max_iter=1000, n_jobs=-1)  \n",
      "Naive Bayes                                            MultinomialNB()  \n"
     ]
    }
   ],
   "source": [
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.svm import SVC\n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.naive_bayes import MultinomialNB\n",
    "\n",
    "# 创建模型比较函数\n",
    "def compare_models(X_train, y_train, X_test, y_test):\n",
    "    models = {\n",
    "        'Logistic Regression': LogisticRegression(max_iter=1000, n_jobs=-1),\n",
    "        'SVM': SVC(kernel='linear', probability=True),\n",
    "        'Random Forest': RandomForestClassifier(n_estimators=100, n_jobs=-1),\n",
    "        'Naive Bayes': MultinomialNB(),\n",
    "        'XGBoost': XGBClassifier(objective='multi:softprob', eval_metric='mlogloss', random_state=42)\n",
    "    }\n",
    "    \n",
    "    results = {}\n",
    "    for name, model in models.items():\n",
    "        print(f\"\\nTraining {name}...\")\n",
    "        model.fit(X_train, y_train)\n",
    "        y_pred = model.predict(X_test)\n",
    "        report = classification_report(y_test, y_pred, output_dict=True)\n",
    "        results[name] = {\n",
    "            'accuracy': report['accuracy'],\n",
    "            'f1_macro': report['macro avg']['f1-score'],\n",
    "            'model': model\n",
    "        }\n",
    "    \n",
    "    return pd.DataFrame(results).T\n",
    "\n",
    "# 比较模型性能\n",
    "model_comparison = compare_models(X_train, y_train, X_test, y_test)\n",
    "print(model_comparison.sort_values('f1_macro', ascending=False))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0d209e9d-c2b0-4889-afdd-06542c8f8f23",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "开始LightGBM网格搜索...\n",
      "Fitting 5 folds for each of 19683 candidates, totalling 98415 fits\n"
     ]
    }
   ],
   "source": [
    "import lightgbm as lgb\n",
    "from sklearn.model_selection import GridSearchCV\n",
    "\n",
    "# LightGBM数据集转换\n",
    "lgb_train = lgb.Dataset(X_train, y_train)\n",
    "lgb_test = lgb.Dataset(X_test, y_test, reference=lgb_train)\n",
    "\n",
    "# 定义参数网格\n",
    "param_grid = {\n",
    "    'num_leaves': [31, 63, 127],\n",
    "    'max_depth': [-1, 5, 10],\n",
    "    'learning_rate': [0.01, 0.05, 0.1],\n",
    "    'n_estimators': [100, 200, 300],\n",
    "    'min_child_samples': [20, 50, 100],\n",
    "    'subsample': [0.8, 0.9, 1.0],\n",
    "    'colsample_bytree': [0.8, 0.9, 1.0],\n",
    "    'reg_alpha': [0, 0.1, 1],\n",
    "    'reg_lambda': [0.1, 1, 10],\n",
    "    'objective': ['multiclass'],\n",
    "    'metric': ['multi_logloss'],\n",
    "    'num_class': [3]\n",
    "}\n",
    "\n",
    "# 创建LightGBM分类器\n",
    "lgb_clf = lgb.LGBMClassifier(random_state=42, n_jobs=-1)\n",
    "\n",
    "# 网格搜索\n",
    "grid_search = GridSearchCV(\n",
    "    estimator=lgb_clf,\n",
    "    param_grid=param_grid,\n",
    "    cv=5,\n",
    "    scoring='accuracy',\n",
    "    n_jobs=-1,\n",
    "    verbose=2\n",
    ")\n",
    "\n",
    "print(\"开始LightGBM网格搜索...\")\n",
    "grid_search.fit(X_train, y_train)\n",
    "print(\"网格搜索完成!\")\n",
    "\n",
    "# 输出最佳参数\n",
    "print(\"最佳参数组合: \", grid_search.best_params_)\n",
    "print(\"最佳准确率: \", grid_search.best_score_)\n",
    "\n",
    "# 评估最佳模型\n",
    "best_lgb = grid_search.best_estimator_\n",
    "y_pred_lgb = best_lgb.predict(X_test)\n",
    "print(\"\\nLightGBM分类报告：\")\n",
    "print(classification_report(y_test, y_pred_lgb))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5df2b75b-6160-45ab-b360-7104a08058b0",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ccc78dff-2cd8-42b9-8b68-604374335e6e",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "fd3e59c0-a88f-48c5-8100-f8c966c72253",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Building prefix dict from the default dictionary ...\n",
      "Loading model from cache C:\\Users\\17828\\AppData\\Local\\Temp\\jieba.cache\n",
      "Loading model cost 3.362 seconds.\n",
      "Prefix dict has been built successfully.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "开始随机森林参数搜索...\n",
      "Fitting 3 folds for each of 20 candidates, totalling 60 fits\n",
      "参数搜索完成!\n",
      "最佳参数组合:  {'n_estimators': 200, 'min_samples_split': 5, 'min_samples_leaf': 1, 'max_features': 'sqrt', 'max_depth': 30, 'bootstrap': False}\n",
      "最佳准确率:  0.6893280632411067\n",
      "\n",
      "随机森林分类报告：\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       0.82      0.67      0.74        79\n",
      "           1       0.57      0.88      0.69        67\n",
      "           2       0.80      0.14      0.24        28\n",
      "\n",
      "    accuracy                           0.67       174\n",
      "   macro avg       0.73      0.56      0.56       174\n",
      "weighted avg       0.72      0.67      0.64       174\n",
      "\n"
     ]
    },
    {
     "ename": "NameError",
     "evalue": "name 'plot_confusion_matrix' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[2], line 88\u001b[0m\n\u001b[0;32m     85\u001b[0m \u001b[38;5;28mprint\u001b[39m(classification_report(y_test, y_pred_rf))\n\u001b[0;32m     87\u001b[0m \u001b[38;5;66;03m# 7. 混淆矩阵可视化（假设已完成，使用之前的函数）\u001b[39;00m\n\u001b[1;32m---> 88\u001b[0m plot_confusion_matrix(y_test, y_pred_rf, [class_names[i] \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28msorted\u001b[39m(class_names\u001b[38;5;241m.\u001b[39mkeys())])\n\u001b[0;32m     90\u001b[0m \u001b[38;5;66;03m# 8. 特征重要性分析\u001b[39;00m\n\u001b[0;32m     91\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m特征重要性分析...\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
      "\u001b[1;31mNameError\u001b[0m: name 'plot_confusion_matrix' is not defined"
     ]
    }
   ],
   "source": [
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.model_selection import RandomizedSearchCV\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib import rcParams\n",
    "import jieba\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.feature_extraction.text import TfidfVectorizer\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.metrics import classification_report, confusion_matrix\n",
    "import seaborn as sns\n",
    "import xgboost as xgb\n",
    "\n",
    "# 设置Matplotlib支持中文字体显示（以SimHei为例，需确保系统中安装了该字体）\n",
    "rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签\n",
    "rcParams['axes.unicode_minus'] = False  # 用来正常显示负号\n",
    "\n",
    "# 1. 数据预处理\n",
    "def preprocess_text(text):\n",
    "    # 加载停用词表（需准备中文停用词文件）\n",
    "    with open('chinese_stopwords.txt', encoding='utf-8') as f:\n",
    "        stopwords = set(f.read().split())\n",
    "    \n",
    "    words = jieba.cut(text)\n",
    "    return ' '.join([word for word in words if word not in stopwords and len(word) > 1])\n",
    "\n",
    "# 加载数据\n",
    "df = pd.read_csv('final_labeled_comments.csv')\n",
    "df['processed_text'] = df['text'].astype(str).apply(preprocess_text)\n",
    "\n",
    "# 2. 文本分类（情感分析）\n",
    "# 标签编码\n",
    "label_map = {'positive': 0, 'neutral': 1, 'negative': 2}\n",
    "df['label'] = df['最终情感标签'].map(label_map)\n",
    "\n",
    "# TF-IDF向量化\n",
    "tfidf = TfidfVectorizer(max_features=2000)\n",
    "X = tfidf.fit_transform(df['processed_text'])\n",
    "y = df['label']\n",
    "\n",
    "# 拆分数据集\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
    "\n",
    "# 1. 数据预处理（假设已完成，使用之前的预处理数据）\n",
    "# X_train, X_test, y_train, y_test 已经定义\n",
    "\n",
    "# 2. 定义随机森林分类器\n",
    "rf_clf = RandomForestClassifier(random_state=42, n_jobs=-1)\n",
    "\n",
    "# 3. 定义参数分布\n",
    "param_dist = {\n",
    "    'n_estimators': [100, 200, 300],  # 树的数量\n",
    "    'max_depth': [None, 10, 20, 30],  # 树的最大深度\n",
    "    'min_samples_split': [2, 5, 10],  # 内部节点再划分所需最小样本数\n",
    "    'min_samples_leaf': [1, 2, 4],    # 叶节点所需最小样本数\n",
    "    'max_features': ['sqrt', 'log2'],  # 寻找最佳分割时要考虑的特征数量\n",
    "    'bootstrap': [True, False]        # 是否使用bootstrap采样\n",
    "}\n",
    "\n",
    "# 4. 随机搜索\n",
    "random_search = RandomizedSearchCV(\n",
    "    estimator=rf_clf,\n",
    "    param_distributions=param_dist,\n",
    "    n_iter=20,  # 随机搜索的迭代次数\n",
    "    cv=3,       # 交叉验证的折数\n",
    "    scoring='accuracy',\n",
    "    n_jobs=-1,\n",
    "    verbose=1,\n",
    "    random_state=42\n",
    ")\n",
    "\n",
    "print(\"开始随机森林参数搜索...\")\n",
    "random_search.fit(X_train, y_train)\n",
    "print(\"参数搜索完成!\")\n",
    "\n",
    "# 5. 输出最佳参数\n",
    "print(\"最佳参数组合: \", random_search.best_params_)\n",
    "print(\"最佳准确率: \", random_search.best_score_)\n",
    "\n",
    "# 6. 评估最佳模型\n",
    "best_rf = random_search.best_estimator_\n",
    "y_pred_rf = best_rf.predict(X_test)\n",
    "print(\"\\n随机森林分类报告：\")\n",
    "print(classification_report(y_test, y_pred_rf))\n",
    "\n",
    "# 7. 混淆矩阵可视化（假设已完成，使用之前的函数）\n",
    "plot_confusion_matrix(y_test, y_pred_rf, [class_names[i] for i in sorted(class_names.keys())])\n",
    "\n",
    "# 8. 特征重要性分析\n",
    "print(\"\\n特征重要性分析...\")\n",
    "importance = best_rf.feature_importances_\n",
    "\n",
    "# 创建特征重要性DataFrame\n",
    "feature_importance = pd.DataFrame({\n",
    "    'feature': feature_names,\n",
    "    'importance': importance\n",
    "}).sort_values('importance', ascending=False)\n",
    "\n",
    "# 显示最重要的20个特征\n",
    "print(\"\\n最重要的20个特征：\")\n",
    "print(feature_importance.head(20))\n",
    "\n",
    "# 可视化最重要的20个特征\n",
    "plt.figure(figsize=(10, 6))\n",
    "feature_importance.head(20).plot(kind='barh', x='feature', y='importance', color='skyblue')\n",
    "plt.title('最重要的20个特征')\n",
    "plt.xlabel('重要性得分')\n",
    "plt.ylabel('特征词')\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "6e39232c-d8df-482a-babf-5a6a0fa61c27",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "类别分布:\n",
      "最终情感标签\n",
      "positive    420\n",
      "neutral     318\n",
      "negative    128\n",
      "Name: count, dtype: int64\n",
      "\n",
      "类别权重: {0: 0.6873015873015873, 1: 0.9077568134171907, 2: 2.2552083333333335}\n",
      "\n",
      "训练模型: tfidf_rf\n",
      "Fitting 5 folds for each of 20 candidates, totalling 100 fits\n",
      "tfidf_rf 最佳参数: {'tfidf__max_features': 5000, 'rf__n_estimators': 200, 'rf__min_samples_split': 5, 'rf__min_samples_leaf': 1, 'rf__max_features': 'sqrt', 'rf__max_depth': None}\n",
      "tfidf_rf 测试集F1分数: 0.6744\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       0.93      0.65      0.77        84\n",
      "           1       0.57      0.91      0.70        64\n",
      "           2       0.46      0.23      0.31        26\n",
      "\n",
      "    accuracy                           0.68       174\n",
      "   macro avg       0.65      0.60      0.59       174\n",
      "weighted avg       0.73      0.68      0.67       174\n",
      "\n",
      "\n",
      "训练模型: count_rf\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "argument of type 'int' is not iterable",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[11], line 109\u001b[0m\n\u001b[0;32m    107\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m k, v \u001b[38;5;129;01min\u001b[39;00m param_dist\u001b[38;5;241m.\u001b[39mitems():\n\u001b[0;32m    108\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(v, \u001b[38;5;28mlist\u001b[39m) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(v[\u001b[38;5;241m0\u001b[39m], \u001b[38;5;28mstr\u001b[39m) \u001b[38;5;129;01mand\u001b[39;00m v[\u001b[38;5;241m0\u001b[39m]\u001b[38;5;241m.\u001b[39mstartswith(param_prefix\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m__\u001b[39m\u001b[38;5;124m'\u001b[39m)[\u001b[38;5;241m0\u001b[39m]):\n\u001b[1;32m--> 109\u001b[0m         adjusted_param_dist[k] \u001b[38;5;241m=\u001b[39m [param_prefix \u001b[38;5;241m+\u001b[39m p\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m__\u001b[39m\u001b[38;5;124m'\u001b[39m)[\u001b[38;5;241m1\u001b[39m] \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m__\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01min\u001b[39;00m p \u001b[38;5;28;01melse\u001b[39;00m p \u001b[38;5;28;01mfor\u001b[39;00m p \u001b[38;5;129;01min\u001b[39;00m v]\n\u001b[0;32m    110\u001b[0m     \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m    111\u001b[0m         adjusted_param_dist[k] \u001b[38;5;241m=\u001b[39m v\n",
      "\u001b[1;31mTypeError\u001b[0m: argument of type 'int' is not iterable"
     ]
    }
   ],
   "source": [
    "import jieba\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib import rcParams\n",
    "from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer\n",
    "from sklearn.model_selection import train_test_split, RandomizedSearchCV\n",
    "from sklearn.metrics import classification_report, confusion_matrix, f1_score\n",
    "from sklearn.ensemble import RandomForestClassifier, VotingClassifier\n",
    "from sklearn.svm import SVC\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.pipeline import Pipeline\n",
    "from sklearn.naive_bayes import MultinomialNB\n",
    "from sklearn.utils.class_weight import compute_class_weight\n",
    "import seaborn as sns\n",
    "import re\n",
    "from zhconv import convert\n",
    "\n",
    "# 设置Matplotlib支持中文字体显示\n",
    "rcParams['font.sans-serif'] = ['SimHei']\n",
    "rcParams['axes.unicode_minus'] = False\n",
    "\n",
    "# 1. 增强版数据预处理\n",
    "def enhanced_preprocess_text(text):\n",
    "    # 简繁转换\n",
    "    text = convert(text, 'zh-hans')\n",
    "    # 去除特殊字符和数字\n",
    "    text = re.sub(r'[^\\w\\s]', '', text)\n",
    "    text = re.sub(r'\\d+', '', text)\n",
    "    # 加载停用词表\n",
    "    with open('chinese_stopwords.txt', encoding='utf-8') as f:\n",
    "        stopwords = set(f.read().split())\n",
    "    # 加载自定义词典（如果有）\n",
    "    # jieba.load_userdict('bubble_tea_terms.txt')\n",
    "    \n",
    "    words = jieba.cut(text)\n",
    "    return ' '.join([word for word in words if word not in stopwords and len(word) > 1])\n",
    "\n",
    "# 加载数据\n",
    "df = pd.read_csv('final_labeled_comments.csv')\n",
    "df['processed_text'] = df['text'].astype(str).apply(enhanced_preprocess_text)\n",
    "\n",
    "# 检查类别分布并计算类别权重\n",
    "print(\"类别分布:\")\n",
    "class_counts = df['最终情感标签'].value_counts()\n",
    "df['label'] = df['最终情感标签'].map(label_map)\n",
    "\n",
    "print(class_counts)\n",
    "class_weights = compute_class_weight('balanced', classes=np.unique(df['label']), y=df['label'])\n",
    "class_weight_dict = dict(zip(np.unique(df['label']), class_weights))\n",
    "print(\"\\n类别权重:\", class_weight_dict)\n",
    "\n",
    "# 2. 文本分类（情感分析）\n",
    "# 标签编码\n",
    "label_map = {'positive': 0, 'neutral': 1, 'negative': 2}\n",
    "df['label'] = df['最终情感标签'].map(label_map)\n",
    "\n",
    "# 3. 尝试不同的文本表示方法和模型\n",
    "pipelines = [\n",
    "    ('tfidf_rf', Pipeline([\n",
    "        ('tfidf', TfidfVectorizer(max_features=5000, ngram_range=(1, 2))),\n",
    "        ('rf', RandomForestClassifier(random_state=42, n_jobs=-1, class_weight='balanced'))\n",
    "    ])),\n",
    "    ('count_rf', Pipeline([\n",
    "        ('count', CountVectorizer(max_features=5000, ngram_range=(1, 2))),\n",
    "        ('rf', RandomForestClassifier(random_state=42, n_jobs=-1, class_weight='balanced'))\n",
    "    ])),\n",
    "    ('tfidf_svm', Pipeline([\n",
    "        ('tfidf', TfidfVectorizer(max_features=5000, ngram_range=(1, 2))),\n",
    "        ('svm', SVC(class_weight='balanced', probability=True, random_state=42))\n",
    "    ])),\n",
    "    ('tfidf_lr', Pipeline([\n",
    "        ('tfidf', TfidfVectorizer(max_features=5000, ngram_range=(1, 2))),\n",
    "        ('lr', LogisticRegression(class_weight='balanced', max_iter=1000, random_state=42))\n",
    "    ])),\n",
    "    ('tfidf_nb', Pipeline([\n",
    "        ('tfidf', TfidfVectorizer(max_features=5000, ngram_range=(1, 2))),\n",
    "        ('nb', MultinomialNB())\n",
    "    ]))\n",
    "]\n",
    "\n",
    "# 4. 数据集拆分\n",
    "X = df['processed_text']\n",
    "y = df['label']\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)\n",
    "\n",
    "# 5. 模型训练与评估\n",
    "results = []\n",
    "best_models = []\n",
    "\n",
    "for name, pipeline in pipelines:\n",
    "    print(f\"\\n训练模型: {name}\")\n",
    "    \n",
    "    # 根据模型类型设置参数分布\n",
    "    if 'rf' in name:\n",
    "        param_dist = {\n",
    "            'tfidf__max_features': [3000, 5000] if 'tfidf' in name else ['count__max_features', 3000, 5000],\n",
    "            'rf__n_estimators': [100, 200, 300],\n",
    "            'rf__max_depth': [None, 10, 20, 30],\n",
    "            'rf__min_samples_split': [2, 5],\n",
    "            'rf__min_samples_leaf': [1, 2],\n",
    "            'rf__max_features': ['sqrt', 'log2']\n",
    "        }\n",
    "        # 修正参数前缀\n",
    "        param_prefix = 'tfidf__' if 'tfidf' in name else 'count__'\n",
    "        adjusted_param_dist = {}\n",
    "        for k, v in param_dist.items():\n",
    "            if isinstance(v, list) and isinstance(v[0], str) and v[0].startswith(param_prefix.split('__')[0]):\n",
    "                adjusted_param_dist[k] = [param_prefix + p.split('__')[1] if '__' in p else p for p in v]\n",
    "            else:\n",
    "                adjusted_param_dist[k] = v\n",
    "        param_dist = adjusted_param_dist\n",
    "        \n",
    "    elif 'svm' in name:\n",
    "        param_dist = {\n",
    "            'tfidf__max_features': [3000, 5000],\n",
    "            'svm__C': [0.1, 1, 10],\n",
    "            'svm__gamma': ['scale', 'auto', 0.01, 0.1],\n",
    "            'svm__kernel': ['linear', 'rbf']\n",
    "        }\n",
    "    elif 'lr' in name:\n",
    "        param_dist = {\n",
    "            'tfidf__max_features': [3000, 5000],\n",
    "            'lr__C': [0.1, 1, 10],\n",
    "            'lr__penalty': ['l1', 'l2'],\n",
    "            'lr__solver': ['liblinear']\n",
    "        }\n",
    "    elif 'nb' in name:\n",
    "        param_dist = {\n",
    "            'tfidf__max_features': [3000, 5000],\n",
    "            'nb__alpha': [0.1, 0.5, 1.0]\n",
    "        }\n",
    "    \n",
    "    random_search = RandomizedSearchCV(\n",
    "        estimator=pipeline,\n",
    "        param_distributions=param_dist,\n",
    "        n_iter=20,\n",
    "        cv=5,\n",
    "        scoring='f1_weighted',\n",
    "        n_jobs=-1,\n",
    "        verbose=1,\n",
    "        random_state=42\n",
    "    )\n",
    "    \n",
    "    try:\n",
    "        random_search.fit(X_train, y_train)\n",
    "        \n",
    "        # 评估模型\n",
    "        y_pred = random_search.predict(X_test)\n",
    "        report = classification_report(y_test, y_pred, output_dict=True)\n",
    "        f1 = f1_score(y_test, y_pred, average='weighted')\n",
    "        \n",
    "        results.append({\n",
    "            'model': name,\n",
    "            'best_params': random_search.best_params_,\n",
    "            'f1_score': f1,\n",
    "            'report': report\n",
    "        })\n",
    "        \n",
    "        best_models.append((name, random_search.best_estimator_))\n",
    "        \n",
    "        print(f\"{name} 最佳参数: {random_search.best_params_}\")\n",
    "        print(f\"{name} 测试集F1分数: {f1:.4f}\")\n",
    "        print(classification_report(y_test, y_pred))\n",
    "    except Exception as e:\n",
    "        print(f\"训练 {name} 模型时出错: {e}\")\n",
    "        continue\n",
    "\n",
    "# 6. 选择最佳模型\n",
    "if results:\n",
    "    results_df = pd.DataFrame(results)\n",
    "    best_model_info = results_df.sort_values('f1_score', ascending=False).iloc[0]\n",
    "    best_model_name = best_model_info['model']\n",
    "    best_model = [m for m in best_models if m[0] == best_model_name][0][1]\n",
    "\n",
    "    print(f\"\\n最佳模型: {best_model_name}\")\n",
    "    print(f\"最佳F1分数: {best_model_info['f1_score']:.4f}\")\n",
    "\n",
    "    # 7. 模型融合（集成学习）\n",
    "    print(\"\\n尝试模型融合...\")\n",
    "    # 选择前两个表现最好的模型\n",
    "    if len(best_models) >= 2:\n",
    "        top_models = [m[1] for m in best_models[:2]]\n",
    "\n",
    "        ensemble = VotingClassifier(\n",
    "            estimators=[(f'model_{i}', model) for i, model in enumerate(top_models)],\n",
    "            voting='soft'\n",
    "        )\n",
    "\n",
    "        try:\n",
    "            ensemble.fit(X_train, y_train)\n",
    "            y_pred_ensemble = ensemble.predict(X_test)\n",
    "            ensemble_f1 = f1_score(y_test, y_pred_ensemble, average='weighted')\n",
    "\n",
    "            print(\"集成模型分类报告:\")\n",
    "            print(classification_report(y_test, y_pred_ensemble))\n",
    "            print(f\"集成模型F1分数: {ensemble_f1:.4f}\")\n",
    "        except Exception as e:\n",
    "            print(f\"模型融合时出错: {e}\")\n",
    "\n",
    "    # 8. 混淆矩阵可视化\n",
    "    def plot_confusion_matrix(y_true, y_pred, classes, title):\n",
    "        cm = confusion_matrix(y_true, y_pred)\n",
    "        plt.figure(figsize=(8, 6))\n",
    "        sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n",
    "                    xticklabels=classes, yticklabels=classes)\n",
    "        plt.xlabel('预测标签')\n",
    "        plt.ylabel('真实标签')\n",
    "        plt.title(title)\n",
    "        plt.show()\n",
    "\n",
    "    class_names = {0: 'positive', 1: 'neutral', 2: 'negative'}\n",
    "    plot_confusion_matrix(y_test, y_pred_ensemble if 'y_pred_ensemble' in locals() else y_pred, \n",
    "                         [class_names[i] for i in sorted(class_names.keys())], \n",
    "                         f'混淆矩阵 (F1={ensemble_f1 if \"ensemble_f1\" in locals() else best_model_info[\"f1_score\"]:.3f})')\n",
    "\n",
    "    # 9. 特征重要性分析（以最佳模型为例）\n",
    "    if 'rf' in best_model_name:\n",
    "        print(\"\\n特征重要性分析...\")\n",
    "        try:\n",
    "            vectorizer = best_model.named_steps['tfidf'] if 'tfidf' in best_model.named_steps else best_model.named_steps['count']\n",
    "            importance = best_model.named_steps['rf'].feature_importances_\n",
    "            feature_names = vectorizer.get_feature_names_out()\n",
    "            \n",
    "            # 创建特征重要性DataFrame\n",
    "            feature_importance = pd.DataFrame({\n",
    "                'feature': feature_names,\n",
    "                'importance': importance\n",
    "            }).sort_values('importance', ascending=False)\n",
    "\n",
    "            # 显示最重要的20个特征\n",
    "            print(\"\\n最重要的20个特征：\")\n",
    "            print(feature_importance.head(20))\n",
    "\n",
    "            # 可视化最重要的20个特征\n",
    "            plt.figure(figsize=(10, 6))\n",
    "            feature_importance.head(20).plot(kind='barh', x='feature', y='importance', color='skyblue')\n",
    "            plt.title('最重要的20个特征')\n",
    "            plt.xlabel('重要性得分')\n",
    "            plt.ylabel('特征词')\n",
    "            plt.tight_layout()\n",
    "            plt.show()\n",
    "        except Exception as e:\n",
    "            print(f\"特征重要性分析出错: {e}\")\n",
    "\n",
    "    # 10. 输出最终评估结果\n",
    "    print(\"\\n最终模型评估结果:\")\n",
    "    print(f\"最佳单一模型: {best_model_name}, F1分数: {best_model_info['f1_score']:.4f}\")\n",
    "    if 'ensemble_f1' in locals():\n",
    "        print(f\"集成模型F1分数: {ensemble_f1:.4f}\")\n",
    "\n",
    "    # 检查是否达到优秀标准\n",
    "    if 'ensemble_f1' in locals() and ensemble_f1 >= 0.8:\n",
    "        print(\"恭喜！达到优秀标准(F1≥0.8)\")\n",
    "    elif best_model_info['f1_score'] >= 0.8:\n",
    "        print(\"恭喜！最佳单一模型达到优秀标准(F1≥0.8)\")\n",
    "    else:\n",
    "        print(\"未达到优秀标准，建议进一步优化:\")\n",
    "        print(\"1. 尝试更多的文本特征（如情感词典、词向量）\")\n",
    "        print(\"2. 收集更多数据或进行数据增强\")\n",
    "        print(\"3. 尝试深度学习模型（如BERT、LSTM）\")\n",
    "        print(\"4. 进行更细致的类别平衡处理\")\n",
    "else:\n",
    "    print(\"没有成功训练任何模型，请检查数据和参数设置\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "8dfa8c10-2330-4cac-8c93-daaa4fbba3e1",
   "metadata": {},
   "outputs": [
    {
     "ename": "RuntimeError",
     "evalue": "Failed to import transformers.models.bert.modeling_bert because of the following error (look up to see its traceback):\nTraceback (most recent call last):\n  File \"C:\\Users\\17828\\AppData\\Roaming\\Python\\Python312\\site-packages\\tensorflow\\python\\pywrap_tensorflow.py\", line 73, in <module>\n    from tensorflow.python._pywrap_tensorflow_internal import *\nImportError: DLL load failed while importing _pywrap_tensorflow_internal: 动态链接库(DLL)初始化例程失败。\n\n\nFailed to load the native TensorFlow runtime.\nSee https://www.tensorflow.org/install/errors for some common causes and solutions.\nIf you need help, create an issue at https://github.com/tensorflow/tensorflow/issues and include the entire stack trace above this error message.",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mImportError\u001b[0m                               Traceback (most recent call last)",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\tensorflow\\python\\pywrap_tensorflow.py:73\u001b[0m\n\u001b[0;32m     72\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m---> 73\u001b[0m   \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtensorflow\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpython\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m_pywrap_tensorflow_internal\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;241m*\u001b[39m\n\u001b[0;32m     74\u001b[0m \u001b[38;5;66;03m# This try catch logic is because there is no bazel equivalent for py_extension.\u001b[39;00m\n\u001b[0;32m     75\u001b[0m \u001b[38;5;66;03m# Externally in opensource we must enable exceptions to load the shared object\u001b[39;00m\n\u001b[0;32m     76\u001b[0m \u001b[38;5;66;03m# by exposing the PyInit symbols with pybind. This error will only be\u001b[39;00m\n\u001b[0;32m     77\u001b[0m \u001b[38;5;66;03m# caught internally or if someone changes the name of the target _pywrap_tensorflow_internal.\u001b[39;00m\n\u001b[0;32m     78\u001b[0m \n\u001b[0;32m     79\u001b[0m \u001b[38;5;66;03m# This logic is used in other internal projects using py_extension.\u001b[39;00m\n",
      "\u001b[1;31mImportError\u001b[0m: DLL load failed while importing _pywrap_tensorflow_internal: 动态链接库(DLL)初始化例程失败。",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[1;31mImportError\u001b[0m                               Traceback (most recent call last)",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\transformers\\utils\\import_utils.py:1967\u001b[0m, in \u001b[0;36m_LazyModule._get_module\u001b[1;34m(self, module_name)\u001b[0m\n\u001b[0;32m   1966\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m-> 1967\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m importlib\u001b[38;5;241m.\u001b[39mimport_module(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m+\u001b[39m module_name, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m)\n\u001b[0;32m   1968\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
      "File \u001b[1;32mC:\\ProgramData\\anaconda3\\Lib\\importlib\\__init__.py:90\u001b[0m, in \u001b[0;36mimport_module\u001b[1;34m(name, package)\u001b[0m\n\u001b[0;32m     89\u001b[0m         level \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[1;32m---> 90\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m _bootstrap\u001b[38;5;241m.\u001b[39m_gcd_import(name[level:], package, level)\n",
      "File \u001b[1;32m<frozen importlib._bootstrap>:1387\u001b[0m, in \u001b[0;36m_gcd_import\u001b[1;34m(name, package, level)\u001b[0m\n",
      "File \u001b[1;32m<frozen importlib._bootstrap>:1360\u001b[0m, in \u001b[0;36m_find_and_load\u001b[1;34m(name, import_)\u001b[0m\n",
      "File \u001b[1;32m<frozen importlib._bootstrap>:1331\u001b[0m, in \u001b[0;36m_find_and_load_unlocked\u001b[1;34m(name, import_)\u001b[0m\n",
      "File \u001b[1;32m<frozen importlib._bootstrap>:935\u001b[0m, in \u001b[0;36m_load_unlocked\u001b[1;34m(spec)\u001b[0m\n",
      "File \u001b[1;32m<frozen importlib._bootstrap_external>:995\u001b[0m, in \u001b[0;36mexec_module\u001b[1;34m(self, module)\u001b[0m\n",
      "File \u001b[1;32m<frozen importlib._bootstrap>:488\u001b[0m, in \u001b[0;36m_call_with_frames_removed\u001b[1;34m(f, *args, **kwds)\u001b[0m\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\transformers\\models\\bert\\modeling_bert.py:47\u001b[0m\n\u001b[0;32m     36\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mmodeling_outputs\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m (\n\u001b[0;32m     37\u001b[0m     BaseModelOutputWithPastAndCrossAttentions,\n\u001b[0;32m     38\u001b[0m     BaseModelOutputWithPoolingAndCrossAttentions,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m     45\u001b[0m     TokenClassifierOutput,\n\u001b[0;32m     46\u001b[0m )\n\u001b[1;32m---> 47\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mmodeling_utils\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m PreTrainedModel\n\u001b[0;32m     48\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpytorch_utils\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m apply_chunking_to_forward, find_pruneable_heads_and_indices, prune_linear_layer\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\transformers\\modeling_utils.py:69\u001b[0m\n\u001b[0;32m     65\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mintegrations\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mtensor_parallel\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m (\n\u001b[0;32m     66\u001b[0m     SUPPORTED_TP_STYLES,\n\u001b[0;32m     67\u001b[0m     shard_and_distribute_module,\n\u001b[0;32m     68\u001b[0m )\n\u001b[1;32m---> 69\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mloss\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mloss_utils\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m LOSS_MAPPING\n\u001b[0;32m     70\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpytorch_utils\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m (  \u001b[38;5;66;03m# noqa: F401\u001b[39;00m\n\u001b[0;32m     71\u001b[0m     Conv1D,\n\u001b[0;32m     72\u001b[0m     apply_chunking_to_forward,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m     77\u001b[0m     prune_linear_layer,\n\u001b[0;32m     78\u001b[0m )\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\transformers\\loss\\loss_utils.py:21\u001b[0m\n\u001b[0;32m     19\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtorch\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mnn\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m BCEWithLogitsLoss, MSELoss\n\u001b[1;32m---> 21\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mloss_deformable_detr\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m DeformableDetrForObjectDetectionLoss, DeformableDetrForSegmentationLoss\n\u001b[0;32m     22\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mloss_for_object_detection\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m ForObjectDetectionLoss, ForSegmentationLoss\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\transformers\\loss\\loss_deformable_detr.py:4\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mtorch\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mnn\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mnn\u001b[39;00m\n\u001b[1;32m----> 4\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mimage_transforms\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m center_to_corners_format\n\u001b[0;32m      5\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mutils\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m is_scipy_available\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\transformers\\image_transforms.py:47\u001b[0m\n\u001b[0;32m     46\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m is_tf_available():\n\u001b[1;32m---> 47\u001b[0m     \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mtensorflow\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mtf\u001b[39;00m\n\u001b[0;32m     49\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m is_flax_available():\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\tensorflow\\__init__.py:40\u001b[0m\n\u001b[0;32m     39\u001b[0m \u001b[38;5;66;03m# Do not remove this line; See https://github.com/tensorflow/tensorflow/issues/42596\u001b[39;00m\n\u001b[1;32m---> 40\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtensorflow\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpython\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m pywrap_tensorflow \u001b[38;5;28;01mas\u001b[39;00m _pywrap_tensorflow  \u001b[38;5;66;03m# pylint: disable=unused-import\u001b[39;00m\n\u001b[0;32m     41\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtensorflow\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpython\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mtools\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m module_util \u001b[38;5;28;01mas\u001b[39;00m _module_util\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\tensorflow\\python\\pywrap_tensorflow.py:88\u001b[0m\n\u001b[0;32m     87\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mImportError\u001b[39;00m:\n\u001b[1;32m---> 88\u001b[0m   \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mImportError\u001b[39;00m(\n\u001b[0;32m     89\u001b[0m       \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mtraceback\u001b[38;5;241m.\u001b[39mformat_exc()\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m     90\u001b[0m       \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mFailed to load the native TensorFlow runtime.\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m     91\u001b[0m       \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mSee https://www.tensorflow.org/install/errors \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m     92\u001b[0m       \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mfor some common causes and solutions.\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m     93\u001b[0m       \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mIf you need help, create an issue \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m     94\u001b[0m       \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mat https://github.com/tensorflow/tensorflow/issues \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m     95\u001b[0m       \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mand include the entire stack trace above this error message.\u001b[39m\u001b[38;5;124m'\u001b[39m)\n",
      "\u001b[1;31mImportError\u001b[0m: Traceback (most recent call last):\n  File \"C:\\Users\\17828\\AppData\\Roaming\\Python\\Python312\\site-packages\\tensorflow\\python\\pywrap_tensorflow.py\", line 73, in <module>\n    from tensorflow.python._pywrap_tensorflow_internal import *\nImportError: DLL load failed while importing _pywrap_tensorflow_internal: 动态链接库(DLL)初始化例程失败。\n\n\nFailed to load the native TensorFlow runtime.\nSee https://www.tensorflow.org/install/errors for some common causes and solutions.\nIf you need help, create an issue at https://github.com/tensorflow/tensorflow/issues and include the entire stack trace above this error message.",
      "\nThe above exception was the direct cause of the following exception:\n",
      "\u001b[1;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[12], line 2\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mtorch\u001b[39;00m\n\u001b[1;32m----> 2\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtransformers\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m BertTokenizer, BertForSequenceClassification, Trainer, TrainingArguments\n\u001b[0;32m      3\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtransformers\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m InputExample, InputFeatures\n\u001b[0;32m      4\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpandas\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mpd\u001b[39;00m\n",
      "File \u001b[1;32m<frozen importlib._bootstrap>:1412\u001b[0m, in \u001b[0;36m_handle_fromlist\u001b[1;34m(module, fromlist, import_, recursive)\u001b[0m\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\transformers\\utils\\import_utils.py:1956\u001b[0m, in \u001b[0;36m_LazyModule.__getattr__\u001b[1;34m(self, name)\u001b[0m\n\u001b[0;32m   1954\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m name \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_class_to_module\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[0;32m   1955\u001b[0m     module \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_module(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_class_to_module[name])\n\u001b[1;32m-> 1956\u001b[0m     value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m(module, name)\n\u001b[0;32m   1957\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m name \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_modules:\n\u001b[0;32m   1958\u001b[0m     value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_module(name)\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\transformers\\utils\\import_utils.py:1955\u001b[0m, in \u001b[0;36m_LazyModule.__getattr__\u001b[1;34m(self, name)\u001b[0m\n\u001b[0;32m   1953\u001b[0m     value \u001b[38;5;241m=\u001b[39m Placeholder\n\u001b[0;32m   1954\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m name \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_class_to_module\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m-> 1955\u001b[0m     module \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_module(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_class_to_module[name])\n\u001b[0;32m   1956\u001b[0m     value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m(module, name)\n\u001b[0;32m   1957\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m name \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_modules:\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\transformers\\utils\\import_utils.py:1969\u001b[0m, in \u001b[0;36m_LazyModule._get_module\u001b[1;34m(self, module_name)\u001b[0m\n\u001b[0;32m   1967\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m importlib\u001b[38;5;241m.\u001b[39mimport_module(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m+\u001b[39m module_name, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m)\n\u001b[0;32m   1968\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m-> 1969\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\n\u001b[0;32m   1970\u001b[0m         \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFailed to import \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m.\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmodule_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m because of the following error (look up to see its\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m   1971\u001b[0m         \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m traceback):\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00me\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m   1972\u001b[0m     ) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01me\u001b[39;00m\n",
      "\u001b[1;31mRuntimeError\u001b[0m: Failed to import transformers.models.bert.modeling_bert because of the following error (look up to see its traceback):\nTraceback (most recent call last):\n  File \"C:\\Users\\17828\\AppData\\Roaming\\Python\\Python312\\site-packages\\tensorflow\\python\\pywrap_tensorflow.py\", line 73, in <module>\n    from tensorflow.python._pywrap_tensorflow_internal import *\nImportError: DLL load failed while importing _pywrap_tensorflow_internal: 动态链接库(DLL)初始化例程失败。\n\n\nFailed to load the native TensorFlow runtime.\nSee https://www.tensorflow.org/install/errors for some common causes and solutions.\nIf you need help, create an issue at https://github.com/tensorflow/tensorflow/issues and include the entire stack trace above this error message."
     ]
    }
   ],
   "source": [
    "import torch\n",
    "from transformers import BertTokenizer, BertForSequenceClassification, Trainer, TrainingArguments\n",
    "from transformers import InputExample, InputFeatures\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.metrics import classification_report, confusion_matrix, f1_score\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "from torch.utils.data import Dataset\n",
    "\n",
    "# 设置Matplotlib支持中文字体显示\n",
    "plt.rcParams['font.sans-serif'] = ['SimHei']\n",
    "plt.rcParams['axes.unicode_minus'] = False\n",
    "\n",
    "# 1. 数据预处理\n",
    "def preprocess_text(text):\n",
    "    # 加载停用词表（需准备中文停用词文件）\n",
    "    with open('chinese_stopwords.txt', encoding='utf-8') as f:\n",
    "        stopwords = set(f.read().split())\n",
    "    \n",
    "    # 这里简化处理，实际应用中可能需要更复杂的预处理\n",
    "    words = jieba.cut(text)\n",
    "    return ' '.join([word for word in words if word not in stopwords and len(word) > 1])\n",
    "\n",
    "# 加载数据\n",
    "df = pd.read_csv('final_labeled_comments.csv')\n",
    "df['processed_text'] = df['text'].astype(str).apply(preprocess_text)\n",
    "\n",
    "# 标签编码\n",
    "label_map = {'positive': 0, 'neutral': 1, 'negative': 2}\n",
    "df['label'] = df['最终情感标签'].map(label_map)\n",
    "\n",
    "# 划分数据集\n",
    "train_texts, val_texts, train_labels, val_labels = train_test_split(\n",
    "    df['processed_text'], df['label'], test_size=0.2, random_state=42, stratify=df['label']\n",
    ")\n",
    "\n",
    "# 2. 使用BERT模型\n",
    "tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')\n",
    "\n",
    "# 定义最大序列长度\n",
    "max_length = 128\n",
    "\n",
    "# 将数据转换为BERT输入格式\n",
    "def encode_examples(texts, labels):\n",
    "    input_examples = [InputExample(guid=None, text_a=text, text_b=None, label=label) \n",
    "                     for text, label in zip(texts, labels)]\n",
    "    \n",
    "    def convert_example_to_feature(example):\n",
    "        return InputFeatures(\n",
    "            input_ids=tokenizer.encode(example.text_a, max_length=max_length, padding='max_length', truncation=True),\n",
    "            attention_mask=[1] * len(tokenizer.encode(example.text_a, max_length=max_length, padding='max_length', truncation=True)),\n",
    "            label=example.label\n",
    "        )\n",
    "    \n",
    "    features = [convert_example_to_feature(example) for example in input_examples]\n",
    "    return {\n",
    "        'input_ids': torch.tensor([f.input_ids for f in features], dtype=torch.long),\n",
    "        'attention_mask': torch.tensor([f.attention_mask for f in features], dtype=torch.long),\n",
    "        'labels': torch.tensor([f.label for f in features], dtype=torch.long)\n",
    "    }\n",
    "\n",
    "# 创建PyTorch数据集\n",
    "class CommentDataset(Dataset):\n",
    "    def __init__(self, encodings, labels):\n",
    "        self.encodings = encodings\n",
    "        self.labels = labels\n",
    "    \n",
    "    def __getitem__(self, idx):\n",
    "        item = {key: val[idx] for key, val in self.encodings.items()}\n",
    "        item['labels'] = self.labels[idx]\n",
    "        return item\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self.labels)\n",
    "\n",
    "# 编码训练和验证数据\n",
    "train_encodings = encode_examples(train_texts.tolist(), train_labels.tolist())\n",
    "val_encodings = encode_examples(val_texts.tolist(), val_labels.tolist())\n",
    "\n",
    "train_dataset = CommentDataset(train_encodings, train_labels.to_numpy())\n",
    "val_dataset = CommentDataset(val_encodings, val_labels.to_numpy())\n",
    "\n",
    "# 3. 定义BERT模型\n",
    "model = BertForSequenceClassification.from_pretrained(\n",
    "    'bert-base-chinese',\n",
    "    num_labels=3  # 三分类问题\n",
    ")\n",
    "\n",
    "# 4. 训练参数设置\n",
    "training_args = TrainingArguments(\n",
    "    output_dir='./results',\n",
    "    num_train_epochs=3,\n",
    "    per_device_train_batch_size=16,\n",
    "    per_device_eval_batch_size=16,\n",
    "    warmup_steps=500,\n",
    "    weight_decay=0.01,\n",
    "    logging_dir='./logs',\n",
    "    logging_steps=10,\n",
    "    evaluation_strategy='epoch',\n",
    "    save_strategy='epoch',\n",
    "    load_best_model_at_end=True\n",
    ")\n",
    "\n",
    "# 5. 定义计算F1分数的函数\n",
    "def compute_metrics(pred):\n",
    "    labels = pred.label_ids\n",
    "    preds = pred.predictions.argmax(-1)\n",
    "    f1 = f1_score(labels, preds, average='weighted')\n",
    "    return {\n",
    "        'accuracy': (labels == preds).mean(),\n",
    "        'f1': f1,\n",
    "        'precision': precision_score(labels, preds, average='weighted'),\n",
    "        'recall': recall_score(labels, preds, average='weighted')\n",
    "    }\n",
    "\n",
    "# 注意：需要从sklearn.metrics导入precision_score和recall_score\n",
    "from sklearn.metrics import precision_score, recall_score\n",
    "\n",
    "# 6. 创建Trainer并训练\n",
    "trainer = Trainer(\n",
    "    model=model,\n",
    "    args=training_args,\n",
    "    train_dataset=train_dataset,\n",
    "    eval_dataset=val_dataset,\n",
    "    compute_metrics=compute_metrics\n",
    ")\n",
    "\n",
    "print(\"开始训练BERT模型...\")\n",
    "trainer.train()\n",
    "print(\"训练完成!\")\n",
    "\n",
    "# 7. 评估模型\n",
    "print(\"\\n评估模型性能...\")\n",
    "eval_result = trainer.evaluate()\n",
    "print(f\"验证集评估结果: {eval_result}\")\n",
    "\n",
    "# 8. 预测并生成分类报告\n",
    "predictions = trainer.predict(val_dataset)\n",
    "preds = np.argmax(predictions.predictions, axis=1)\n",
    "\n",
    "print(\"\\nBERT模型分类报告：\")\n",
    "print(classification_report(val_labels, preds, target_names=['positive', 'neutral', 'negative']))\n",
    "\n",
    "# 9. 混淆矩阵可视化\n",
    "def plot_confusion_matrix(y_true, y_pred, classes):\n",
    "    cm = confusion_matrix(y_true, y_pred)\n",
    "    plt.figure(figsize=(8, 6))\n",
    "    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n",
    "                xticklabels=classes, yticklabels=classes)\n",
    "    plt.xlabel('预测标签')\n",
    "    plt.ylabel('真实标签')\n",
    "    plt.title('混淆矩阵')\n",
    "    plt.show()\n",
    "\n",
    "plot_confusion_matrix(val_labels, preds, ['positive', 'neutral', 'negative'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc200d88-876a-45d8-989c-9c0500086c10",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
